- initial import
This commit is contained in:
747
src/engine/endpoint/EP_Account.cpp
Normal file
747
src/engine/endpoint/EP_Account.cpp
Normal file
@@ -0,0 +1,747 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EP_Engine.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include <resip/stack/ExtensionHeader.hxx>
|
||||
#include <resip/stack/Pidf.hxx>
|
||||
#include <resip/stack/PlainContents.hxx>
|
||||
|
||||
#define LOG_SUBSYSTEM "Account"
|
||||
|
||||
#define CONFIG(X) mConfig->at(X)
|
||||
#define CONFIG_EXISTS(X) mConfig->exists(X)
|
||||
|
||||
//#define MODIFY_VIA_BEHIND_NAT
|
||||
|
||||
// NAT decorator
|
||||
class NATDecorator: public resip::MessageDecorator
|
||||
{
|
||||
protected:
|
||||
UserAgent& mUserAgent;
|
||||
resip::SipMessage mMessage;
|
||||
|
||||
resip::Data mViaHost;
|
||||
unsigned short mViaPort;
|
||||
|
||||
resip::Data mContactsHost;
|
||||
resip::Data mContactsScheme;
|
||||
unsigned short mContactsPort;
|
||||
|
||||
public:
|
||||
NATDecorator(UserAgent& endpoint);
|
||||
virtual ~NATDecorator();
|
||||
|
||||
virtual void decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId);
|
||||
virtual void rollbackMessage(resip::SipMessage& msg);
|
||||
virtual MessageDecorator* clone() const;
|
||||
};
|
||||
|
||||
|
||||
NATDecorator::NATDecorator(UserAgent& ua)
|
||||
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
|
||||
{
|
||||
}
|
||||
|
||||
NATDecorator::~NATDecorator()
|
||||
{
|
||||
}
|
||||
|
||||
void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId)
|
||||
{
|
||||
// Make a copy to allow rollback
|
||||
mMessage = msg;
|
||||
|
||||
std::stringstream dump;
|
||||
mMessage.encode(dump);
|
||||
//ICELogDebug(<< "Decorating message: \n" << dump.str());
|
||||
|
||||
// Check From: header and find the account
|
||||
resip::NameAddr from;
|
||||
if (msg.isRequest())
|
||||
from = msg.header(resip::h_From);
|
||||
else
|
||||
from = msg.header(resip::h_To);
|
||||
|
||||
PAccount account = mUserAgent.getAccount(from);
|
||||
if (!account)
|
||||
{
|
||||
ICELogDebug(<< "Bad from header " << from.uri().getAor().c_str() << ". Will skip it");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account->mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||
return;
|
||||
|
||||
if (!account->mExternalAddress.isEmpty())
|
||||
{
|
||||
#ifdef MODIFY_VIA_BEHIND_NAT
|
||||
if (msg.header(resip::h_Vias).size() > 0)
|
||||
{
|
||||
resip::Via& via = msg.header(resip::h_Vias).front();
|
||||
mViaHost = via.sentHost();
|
||||
mViaPort = via.sentPort();
|
||||
|
||||
via.sentHost() = resip::Data(account->mExternalAddress.ip());
|
||||
via.sentPort() = account->mExternalAddress.port();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (msg.header(resip::h_Contacts).size() > 0)
|
||||
{
|
||||
resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
|
||||
mContactsHost = uri.host();
|
||||
mContactsPort = uri.port();
|
||||
mContactsScheme = uri.scheme();
|
||||
|
||||
uri.host() = resip::Data(account->mExternalAddress.ip());
|
||||
uri.port() = account->mExternalAddress.port();
|
||||
if (account->mConfig->at(CONFIG_SIPS).asBool())
|
||||
{
|
||||
//uri.scheme() = "sips";
|
||||
//uri.param(resip::p_transport) = "tls";
|
||||
}
|
||||
|
||||
//uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NATDecorator::rollbackMessage(resip::SipMessage& msg)
|
||||
{
|
||||
// Check From: header and find the account
|
||||
resip::NameAddr from = msg.header(resip::h_From);
|
||||
PAccount account = mUserAgent.getAccount(from);
|
||||
if (!account)
|
||||
return;
|
||||
|
||||
if (!account->mExternalAddress.isEmpty())
|
||||
{
|
||||
#ifdef MODIFY_VIA_BEHIND_NAT
|
||||
if (msg.header(resip::h_Vias).size() > 0)
|
||||
{
|
||||
resip::Via& via = msg.header(resip::h_Vias).front();
|
||||
if ((via.sentHost() == resip::Data(account->mExternalAddress.ip())) &&
|
||||
(via.sentPort() == account->mExternalAddress.port()))
|
||||
{
|
||||
via.sentHost() = mViaHost;
|
||||
via.sentPort() = mViaPort;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (msg.header(resip::h_Contacts).size() > 0)
|
||||
{
|
||||
resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
|
||||
if ((uri.host() == resip::Data(account->mExternalAddress.ip())) &&
|
||||
(uri.port() == account->mExternalAddress.port()))
|
||||
{
|
||||
uri.host() = mContactsHost;
|
||||
uri.port() = mContactsPort;
|
||||
//uri.scheme() = mContactsScheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resip::MessageDecorator* NATDecorator::clone() const
|
||||
{
|
||||
return new NATDecorator(mUserAgent);
|
||||
}
|
||||
|
||||
Account::Account(PVariantMap config, UserAgent& agent)
|
||||
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
||||
mRegistration(NULL)
|
||||
{
|
||||
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
|
||||
mId = Account::generateId();
|
||||
setup(*config);
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
{
|
||||
}
|
||||
|
||||
void Account::setup(VariantMap &config)
|
||||
{
|
||||
// Credentials
|
||||
|
||||
if (!config.exists(CONFIG_USERNAME) || !config.exists(CONFIG_PASSWORD) || !config.exists(CONFIG_DOMAIN))
|
||||
throw Exception(ERR_NO_CREDENTIALS);
|
||||
|
||||
mProfile->clearDigestCredentials();
|
||||
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
|
||||
resip::Data(config[CONFIG_USERNAME].asStdString()),
|
||||
resip::Data(config[CONFIG_PASSWORD].asStdString()));
|
||||
ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() <<
|
||||
", username to " << config[CONFIG_USERNAME].asStdString());
|
||||
|
||||
// Proxy
|
||||
mProfile->unsetOutboundProxy();
|
||||
if (config.exists(CONFIG_PROXY))
|
||||
{
|
||||
if (!config[CONFIG_PROXY].asStdString().empty())
|
||||
{
|
||||
resip::Uri proxyAddr;
|
||||
proxyAddr.host() = resip::Data(config[CONFIG_PROXY].asStdString());
|
||||
proxyAddr.port() = 5060;
|
||||
if (config.exists(CONFIG_PROXYPORT))
|
||||
{
|
||||
if (config[CONFIG_PROXYPORT].asInt())
|
||||
proxyAddr.port() = config[CONFIG_PROXYPORT].asInt();
|
||||
}
|
||||
if (config[CONFIG_SIPS].asBool())
|
||||
proxyAddr.param(resip::p_transport) = "tls";
|
||||
|
||||
mProfile->setOutboundProxy(proxyAddr);
|
||||
}
|
||||
}
|
||||
|
||||
// NAT decorator
|
||||
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
|
||||
|
||||
// Rinstance
|
||||
if (config.exists(CONFIG_INSTANCE_ID))
|
||||
{
|
||||
if (!config[CONFIG_INSTANCE_ID].asStdString().empty())
|
||||
mProfile->setInstanceId(config[CONFIG_INSTANCE_ID].asStdString().c_str());
|
||||
}
|
||||
else
|
||||
mProfile->setInstanceId(resip::Data::Empty);
|
||||
|
||||
if (config.exists(CONFIG_REGID))
|
||||
mProfile->setRegId(config[CONFIG_REGID].asInt());
|
||||
|
||||
if (config.exists(CONFIG_RINSTANCE))
|
||||
mProfile->setRinstanceEnabled(config[CONFIG_RINSTANCE].asBool());
|
||||
else
|
||||
mProfile->setRinstanceEnabled(true);
|
||||
|
||||
if (config.exists(CONFIG_RPORT))
|
||||
mProfile->setRportEnabled(config[CONFIG_RPORT].asBool());
|
||||
else
|
||||
mProfile->setRportEnabled(true);
|
||||
|
||||
// From header
|
||||
resip::NameAddr from;
|
||||
if (config.exists(CONFIG_DISPLAYNAME))
|
||||
from.displayName() = resip::Data(config[CONFIG_DISPLAYNAME].asStdString().c_str());
|
||||
|
||||
from.uri().scheme() = config[CONFIG_SIPS].asBool() ? "sips" : "sip";
|
||||
if (config[CONFIG_DOMAINPORT].asInt() != 0)
|
||||
from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
|
||||
else
|
||||
from.uri().port();// = 5060;
|
||||
|
||||
from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
|
||||
from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());
|
||||
|
||||
mProfile->setDefaultFrom(from);
|
||||
|
||||
if (!CONFIG_EXISTS(CONFIG_REGISTERDURATION))
|
||||
CONFIG(CONFIG_REGISTERDURATION) = UA_REGISTRATION_TIME;
|
||||
}
|
||||
|
||||
int Account::id() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
void Account::start()
|
||||
{
|
||||
ICELogInfo(<< "Starting account " << this->name());
|
||||
if (mRegistrationState != RegistrationState::None)
|
||||
{
|
||||
ICELogInfo(<< "Registration is active or in progress already.");
|
||||
return;
|
||||
}
|
||||
if (!mAgent.mDum)
|
||||
{
|
||||
ICELogInfo(<< "DUM is not started yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create registration
|
||||
mRegistration = new ResipSession(*mAgent.mDum);
|
||||
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||
|
||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||
|
||||
mRegistrationState = RegistrationState::Registering;
|
||||
|
||||
// Send packet here
|
||||
mAgent.mDum->send(regmessage);
|
||||
|
||||
// Check if STUN IP is required
|
||||
bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP);
|
||||
//bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty();
|
||||
if (noStunServerIp)
|
||||
{
|
||||
ICELogInfo(<<"No STUN server name or IP is not specified. Has to resolve/discover STUN server IP.");
|
||||
mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000);
|
||||
mRefreshStunServerIpTimer.isTimeToSend();
|
||||
queryStunServerIp();
|
||||
}
|
||||
}
|
||||
|
||||
void Account::stop()
|
||||
{
|
||||
// Close presence publication
|
||||
if (mPublication.isValid())
|
||||
{
|
||||
mPublication->end();
|
||||
mPublication = resip::ClientPublicationHandle();
|
||||
}
|
||||
|
||||
// Close client subscriptions
|
||||
|
||||
// Close registration
|
||||
if (mRegistrationHandle.isValid())
|
||||
{
|
||||
mRegistrationHandle->removeAll();
|
||||
mRegistrationHandle = resip::ClientRegistrationHandle();
|
||||
}
|
||||
else
|
||||
if (mRegistration)
|
||||
{
|
||||
mRegistration->end();
|
||||
}
|
||||
mRegistration = NULL;
|
||||
mRegistrationState = RegistrationState::None;
|
||||
}
|
||||
|
||||
void Account::refresh()
|
||||
{
|
||||
if (mRegistrationHandle.isValid())
|
||||
{
|
||||
mRegistrationState = RegistrationState::Registering;
|
||||
mRegistrationHandle->requestRefresh();
|
||||
}
|
||||
|
||||
if (mPublication.isValid())
|
||||
{
|
||||
mPublication->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool Account::active()
|
||||
{
|
||||
return mRegistrationState == RegistrationState::Registered ||
|
||||
mRegistrationState == RegistrationState::Registering ||
|
||||
mRegistrationState == RegistrationState::Reregistering;
|
||||
}
|
||||
|
||||
std::string Account::name()
|
||||
{
|
||||
return contact(SecureScheme::Nothing).uri().toString().c_str();
|
||||
}
|
||||
|
||||
Account::RegistrationState Account::registrationState()
|
||||
{
|
||||
return mRegistrationState;
|
||||
}
|
||||
|
||||
void Account::publishPresence(bool online, const std::string& content, int seconds)
|
||||
{
|
||||
if (online == mPresenceOnline && content == mPresenceContent)
|
||||
return;
|
||||
|
||||
mPresenceOnline = online;
|
||||
mPresenceContent = content;
|
||||
|
||||
resip::Pidf p;
|
||||
p.setEntity(contact(SecureScheme::Nothing).uri());
|
||||
p.setSimpleId(resip::Data(CONFIG(CONFIG_PRESENCE_ID).asStdString()));
|
||||
p.setSimpleStatus(online, resip::Data(content));
|
||||
|
||||
if (mPublication.isValid())
|
||||
mPublication->update(&p);
|
||||
else
|
||||
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds));
|
||||
}
|
||||
|
||||
void Account::stopPublish()
|
||||
{
|
||||
if (mPublication.isValid())
|
||||
mPublication->end();
|
||||
}
|
||||
|
||||
PClientObserver Account::observe(const std::string& target, const std::string& package, void* tag)
|
||||
{
|
||||
// Add subscription functionality
|
||||
PClientObserver observer(new ClientObserver());
|
||||
observer->mSession = new ResipSession(*mAgent.mDum);
|
||||
observer->mSession->setRemoteAddress(target);
|
||||
observer->mSession->setTag(tag);
|
||||
observer->mSessionId = observer->mSession->sessionId();
|
||||
observer->mPeer = target;
|
||||
|
||||
resip::SharedPtr<resip::SipMessage> msg;
|
||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
|
||||
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
|
||||
|
||||
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile,
|
||||
resip::Data(package), expires, refresh, observer->mSession);
|
||||
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
|
||||
|
||||
mAgent.mClientObserverMap[observer->mSessionId] = observer;
|
||||
mAgent.mDum->send(msg);
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
/* Queues message to peer with specified mime type. Returns ID of message. */
|
||||
int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag)
|
||||
{
|
||||
ResipSession* s = new ResipSession(*mAgent.mDum);
|
||||
s->setUa(&mAgent);
|
||||
s->setTag(tag);
|
||||
s->setRemoteAddress(peer);
|
||||
|
||||
// Find MIME type
|
||||
resip::Mime type;
|
||||
std::string::size_type p = mime.find('/');
|
||||
if (p != std::string::npos)
|
||||
type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p+1)));
|
||||
else
|
||||
type = resip::Mime(resip::Data(mime), resip::Data());
|
||||
|
||||
resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
|
||||
auto_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type));
|
||||
int result = s->sessionId();
|
||||
msgHandle->page(contentPtr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
resip::NameAddr Account::contact(SecureScheme ss)
|
||||
{
|
||||
resip::NameAddr result;
|
||||
switch (ss)
|
||||
{
|
||||
case SecureScheme::Nothing:
|
||||
break;
|
||||
case SecureScheme::SipsOnly:
|
||||
result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
|
||||
break;
|
||||
case SecureScheme::SipsAndTls:
|
||||
result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
|
||||
if (mConfig->at(CONFIG_SIPS).asBool())
|
||||
result.uri().param(resip::p_transport) = "tls";
|
||||
break;
|
||||
case SecureScheme::TlsOnly:
|
||||
if (mConfig->at(CONFIG_SIPS).asBool())
|
||||
result.uri().param(resip::p_transport) = "tls";
|
||||
break;
|
||||
}
|
||||
|
||||
result.uri().user() = resip::Data(mConfig->at(CONFIG_USERNAME).asStdString());
|
||||
result.uri().host() = resip::Data(mConfig->at(CONFIG_DOMAIN).asStdString());
|
||||
result.uri().port() = mConfig->at(CONFIG_DOMAINPORT).asInt();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Account::queryStunServerIp()
|
||||
{
|
||||
ICELogInfo(<<"Looking for STUN/TURN server IP");
|
||||
|
||||
if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
|
||||
{
|
||||
// Send request to find STUN or TURN service
|
||||
std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + mConfig->at(CONFIG_DOMAIN).asStdString();
|
||||
|
||||
// Start lookup
|
||||
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if host name is ip already
|
||||
std::string server = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString();
|
||||
if (ice::NetworkAddress::isIp(server))
|
||||
{
|
||||
mConfig->at(CONFIG_STUNSERVER_IP) = server;
|
||||
mRefreshStunServerIpTimer.stop();
|
||||
}
|
||||
else
|
||||
mAgent.mStack->getDnsStub().lookup<resip::RR_A>(resip::Data(server), this);
|
||||
}
|
||||
}
|
||||
|
||||
void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
|
||||
{
|
||||
ice::ServerConfig config;
|
||||
ice::NetworkAddress addr;
|
||||
addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString());
|
||||
if (mConfig->at(CONFIG_STUNSERVER_PORT).asInt())
|
||||
addr.setPort(mConfig->at(CONFIG_STUNSERVER_PORT).asInt());
|
||||
else
|
||||
addr.setPort(3478);
|
||||
|
||||
config.mServerList4.push_back(addr);
|
||||
config.mRelay = mConfig->at(CONFIG_RELAY).asBool();
|
||||
if (mConfig->exists(CONFIG_ICETIMEOUT))
|
||||
config.mTimeout = mConfig->at(CONFIG_ICETIMEOUT).asInt();
|
||||
|
||||
config.mUsername = mConfig->at(CONFIG_ICEUSERNAME).asStdString();
|
||||
config.mPassword = mConfig->at(CONFIG_ICEPASSWORD).asStdString();
|
||||
|
||||
config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool();
|
||||
config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
|
||||
//config.mDetectNetworkChange = true;
|
||||
//config.mNetworkCheckInterval = 5000;
|
||||
|
||||
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
|
||||
session->mIceStack->setEventHandler(session, this);
|
||||
session->mIceStack->setRole(icerole);
|
||||
}
|
||||
|
||||
void Account::process()
|
||||
{
|
||||
if (mRefreshStunServerIpTimer.isTimeToSend())
|
||||
queryStunServerIp();
|
||||
}
|
||||
|
||||
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
|
||||
{
|
||||
// Save registration handle
|
||||
mRegistrationHandle = h;
|
||||
|
||||
// Copy user info to registration handle
|
||||
for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||
mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));
|
||||
|
||||
// Get the Via
|
||||
const resip::Via& via = response.header(resip::h_Vias).front();
|
||||
|
||||
// Get the sent host
|
||||
const resip::Data& sentHost = via.sentHost();//response.header(h_Contacts).front().uri().host();
|
||||
|
||||
// Get the sentPort
|
||||
int sentPort = via.sentPort();
|
||||
|
||||
const resip::Data& sourceHost = response.getSource().toData(resip::UDP);
|
||||
int rport = 0;
|
||||
if (via.exists(resip::p_rport))
|
||||
rport = via.param(resip::p_rport).port();
|
||||
|
||||
resip::Data received = "";
|
||||
if (via.exists(resip::p_received))
|
||||
received = via.param(resip::p_received);
|
||||
|
||||
bool hostChanged = sentHost != received && received.size() > 0;
|
||||
bool portChanged = sentPort != rport && rport != 0;
|
||||
|
||||
// Save external port and IP address
|
||||
if (received.size() > 0 /*&& mConfig->at(CONFIG_EXTERNALIP).asBool()*/)
|
||||
{
|
||||
mExternalAddress.setIp(received.c_str());
|
||||
mExternalAddress.setPort(rport ? rport : sentPort);
|
||||
|
||||
// Add new external address to domain list
|
||||
if (mAgent.mStack)
|
||||
mAgent.mStack->addAlias(resip::Data(mExternalAddress.ip()), mExternalAddress.port());
|
||||
if (mAgent.mDum)
|
||||
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
||||
}
|
||||
|
||||
const resip::Transport* transport = response.getReceivedTransport();
|
||||
mUsedTransport = transport->transport();
|
||||
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
|
||||
|
||||
// Retry registration for stream based transport too
|
||||
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||
{
|
||||
//mRegistrationHandle->requestRefresh();
|
||||
// Unregister at first
|
||||
mRegistrationHandle->removeAll();
|
||||
mRegistrationState = RegistrationState::Reregistering;
|
||||
return;
|
||||
}
|
||||
|
||||
// So here we registered ok
|
||||
mRegistrationState = RegistrationState::Registered;
|
||||
mAgent.onAccountStart(mAgent.getAccount(this));
|
||||
}
|
||||
|
||||
void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
|
||||
{
|
||||
// Check if this unregistering is a part of rport pr
|
||||
if (mRegistrationState == RegistrationState::Reregistering)
|
||||
{
|
||||
//if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP)
|
||||
{
|
||||
resip::Uri hostport(contact(SecureScheme::TlsOnly).uri());
|
||||
hostport.host() = resip::Data(mExternalAddress.ip());
|
||||
hostport.port() = mExternalAddress.port();
|
||||
if (mUsedTransport != resip::UDP)
|
||||
{
|
||||
const char* transportName = nullptr;
|
||||
switch (mUsedTransport)
|
||||
{
|
||||
case resip::TCP: transportName = "tcp"; break;
|
||||
case resip::TLS: transportName = "tls"; break;
|
||||
}
|
||||
|
||||
hostport.param(resip::p_transport) = resip::Data(transportName);
|
||||
}
|
||||
mProfile->setOverrideHostAndPort(hostport);
|
||||
//mProfile->setDefaultFrom(from);
|
||||
}
|
||||
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
||||
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||
|
||||
mAgent.mDum->send(regmessage);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mRegistration = NULL;
|
||||
mRegistrationState = RegistrationState::None;
|
||||
mRegistrationHandle = resip::ClientRegistrationHandle();
|
||||
mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
void Account::onFailure(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
|
||||
{
|
||||
// Reset registration handle
|
||||
mRegistrationHandle = resip::ClientRegistrationHandle();
|
||||
mRegistrationState = RegistrationState::None;
|
||||
mRegistration = NULL;
|
||||
mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
|
||||
}
|
||||
|
||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
|
||||
{
|
||||
if (result.status == 0)
|
||||
{
|
||||
resip::Data foundAddress = result.records.front().host();
|
||||
ICELogCritical( << "Success to resolve STUN/TURN address to " << foundAddress.c_str());
|
||||
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());
|
||||
|
||||
// Here the IP address of STUN/TURN server is found. If account is registered already - it means account is ready.
|
||||
if (mRegistrationState == RegistrationState::Registered)
|
||||
mAgent.onAccountStart(mAgent.getAccount(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogCritical( << "Failed to resolve STUN or TURN server IP address.");
|
||||
if (mRegistrationState == RegistrationState::Registered)
|
||||
{
|
||||
int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503;
|
||||
mAgent.onAccountStop(mAgent.getAccount(this), startCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||
{
|
||||
if (result.status == 0)
|
||||
{
|
||||
// Find lowest priority
|
||||
int priority = 0x7FFFFFFF;
|
||||
for (size_t i=0; i<result.records.size(); i++)
|
||||
if (result.records[i].priority() < priority)
|
||||
priority = result.records[i].priority();
|
||||
|
||||
size_t index = 0;
|
||||
int weight = 0;
|
||||
|
||||
for (size_t i=0; i<result.records.size(); i++)
|
||||
{
|
||||
if (result.records[i].priority() == priority && result.records[i].weight() >= weight)
|
||||
{
|
||||
index = i;
|
||||
weight = result.records[i].weight();
|
||||
}
|
||||
}
|
||||
|
||||
mConfig->at(CONFIG_STUNSERVER_PORT) = result.records[index].port();
|
||||
|
||||
const char* host = result.records[index].target().c_str();
|
||||
|
||||
ICELogCritical( << "Success to find STUN/TURN server on " << result.records[index].target().c_str() <<
|
||||
":" << (int)result.records[index].port());
|
||||
|
||||
|
||||
if (inet_addr(host) == INADDR_NONE)
|
||||
{
|
||||
// Try to resolve domain name now
|
||||
mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this);
|
||||
//mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(host);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogCritical( << "Failed to find STUN or TURN service for specified domain.");
|
||||
//mAgent::shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool Account::isResponsibleFor(const resip::NameAddr &addr)
|
||||
{
|
||||
std::string user = addr.uri().user().c_str();
|
||||
std::string domain = addr.uri().host().c_str();
|
||||
int p = addr.uri().port();
|
||||
if (mConfig->at(CONFIG_USERNAME).asStdString() == user && mConfig->at(CONFIG_DOMAIN).asStdString() == domain)
|
||||
{
|
||||
// Check if ports are the same or port is not specified at all
|
||||
if (mConfig->exists(CONFIG_DOMAINPORT))
|
||||
return mConfig->at(CONFIG_DOMAINPORT).asInt() == p || !p;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Account::setUserInfo(const UserInfo &info)
|
||||
{
|
||||
mUserInfo = info;
|
||||
if (mRegistrationHandle.isValid())
|
||||
{
|
||||
for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||
mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Account::UserInfo Account::getUserInfo() const
|
||||
{
|
||||
return mUserInfo;
|
||||
}
|
||||
|
||||
resip::AtomicCounter Account::IdGenerator;
|
||||
int Account::generateId()
|
||||
{
|
||||
return IdGenerator.increment();
|
||||
}
|
||||
143
src/engine/endpoint/EP_Account.h
Normal file
143
src/engine/endpoint/EP_Account.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef EP_ACCOUNT_H
|
||||
#define EP_ACCOUNT_H
|
||||
|
||||
#include "../helper/HL_Pointer.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "ice/ICEAddress.h"
|
||||
#include "ice/ICETime.h"
|
||||
#include "ice/ICEBox.h"
|
||||
#include "resip/dum/UserProfile.hxx"
|
||||
#include "resip/dum/ClientRegistration.hxx"
|
||||
#include "resip/dum/ClientPublication.hxx"
|
||||
#include "resip/stack/DnsInterface.hxx"
|
||||
#include "resip/stack/NameAddr.hxx"
|
||||
|
||||
#include "EP_Observer.h"
|
||||
|
||||
class UserAgent;
|
||||
class Session;
|
||||
|
||||
class Account: public resip::DnsResultSink
|
||||
{
|
||||
friend class UserAgent;
|
||||
friend class NATDecorator;
|
||||
public:
|
||||
Account(PVariantMap config, UserAgent& agent);
|
||||
~Account();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void refresh();
|
||||
bool active();
|
||||
int id() const;
|
||||
|
||||
enum class RegistrationState
|
||||
{
|
||||
None,
|
||||
Registering,
|
||||
Reregistering,
|
||||
Registered,
|
||||
Unregistering
|
||||
};
|
||||
RegistrationState registrationState();
|
||||
|
||||
/* Publishes new presence information */
|
||||
void publishPresence(bool online, const std::string& content, int seconds = 600);
|
||||
|
||||
/* Stops publishing of presence */
|
||||
void stopPublish();
|
||||
|
||||
/* Starts observing on specified target / package */
|
||||
PClientObserver observe(const std::string& target, const std::string& package, void* tag);
|
||||
|
||||
/* Queues message to peer with specified mime type. Returns ID of message. */
|
||||
int sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag);
|
||||
|
||||
/* Returns name of account - <sip:user@domain> */
|
||||
std::string name();
|
||||
|
||||
/* Updates account with configuration */
|
||||
void setup(VariantMap& config);
|
||||
|
||||
/* Returns corresponding resiprocate profile */
|
||||
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
||||
|
||||
typedef std::map<std::string, std::string> UserInfo;
|
||||
void setUserInfo(const UserInfo& info);
|
||||
UserInfo getUserInfo() const;
|
||||
|
||||
protected:
|
||||
PVariantMap mConfig;
|
||||
|
||||
// Registration
|
||||
ResipSession* mRegistration;
|
||||
resip::ClientRegistrationHandle mRegistrationHandle;
|
||||
resip::ClientPublicationHandle mPublication;
|
||||
resip::TransportType mUsedTransport;
|
||||
|
||||
RegistrationState mRegistrationState;
|
||||
|
||||
ice::NetworkAddress mExternalAddress;
|
||||
resip::SharedPtr<resip::UserProfile> mProfile;
|
||||
UserAgent& mAgent;
|
||||
bool mPresenceOnline;
|
||||
std::string mPresenceContent;
|
||||
|
||||
// Timer to refresh STUN server IP
|
||||
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
|
||||
|
||||
// Cached auth
|
||||
resip::Auth mCachedAuth;
|
||||
|
||||
// Id of account
|
||||
int mId;
|
||||
|
||||
// User info about current state
|
||||
UserInfo mUserInfo;
|
||||
|
||||
// List of client subscriptions sent from this account
|
||||
typedef std::set<PClientObserver> ClientObserverSet;
|
||||
ClientObserverSet mClientObserverSet;
|
||||
|
||||
|
||||
void process();
|
||||
// Method queries new stun server ip from dns (if stun server is specified as dns name)
|
||||
void queryStunServerIp();
|
||||
|
||||
bool isResponsibleFor(const resip::NameAddr& addr);
|
||||
enum class SecureScheme
|
||||
{
|
||||
SipsAndTls,
|
||||
SipsOnly,
|
||||
TlsOnly,
|
||||
Nothing
|
||||
};
|
||||
|
||||
resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly);
|
||||
|
||||
// This method prepares configuration, creates ice stack and sets ownership to session
|
||||
void prepareIceStack(Session* session, ice::AgentRole role);
|
||||
void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
|
||||
void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
|
||||
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response);
|
||||
|
||||
#pragma region DnsResultSink implementation
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&);
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&);
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&);
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
|
||||
#pragma endregion
|
||||
|
||||
static int generateId();
|
||||
static resip::AtomicCounter IdGenerator;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Account> PAccount;
|
||||
|
||||
#endif // EP_ACCOUNT_H
|
||||
380
src/engine/endpoint/EP_AudioProvider.cpp
Normal file
380
src/engine/endpoint/EP_AudioProvider.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EP_AudioProvider.h"
|
||||
#include "EP_Engine.h"
|
||||
#include "../media/MT_Box.h"
|
||||
#include "../media/MT_AudioStream.h"
|
||||
#include "../media/MT_SrtpHelper.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
#include "../helper/HL_Rtp.h"
|
||||
#include "../helper/HL_StreamState.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_String.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "AudioProvider"
|
||||
|
||||
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
||||
:mUserAgent(agent), mTerminal(terminal), mState(0),
|
||||
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
|
||||
{
|
||||
mActive = mfActive;
|
||||
mRemoteState = msSendRecv;
|
||||
mActiveStream = mTerminal.createStream(MT::Stream::Audio, mUserAgent.config());
|
||||
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
|
||||
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
|
||||
mSrtpSuite = SRTP_NONE;
|
||||
setState((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
|
||||
}
|
||||
|
||||
AudioProvider::~AudioProvider()
|
||||
{
|
||||
}
|
||||
|
||||
std::string AudioProvider::streamName()
|
||||
{
|
||||
return "audio";
|
||||
}
|
||||
|
||||
std::string AudioProvider::streamProfile()
|
||||
{
|
||||
if (mState & (int)StreamState::Srtp)
|
||||
return "RTP/SAVP";
|
||||
else
|
||||
return "RTP/AVP";
|
||||
}
|
||||
|
||||
// Sets destination IP address
|
||||
void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return;
|
||||
|
||||
mActiveStream->setDestination(addr);
|
||||
}
|
||||
|
||||
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer, void* userTag)
|
||||
{
|
||||
mMediaObserver = observer;
|
||||
mMediaObserverTag = userTag;
|
||||
if (mActiveStream)
|
||||
mActiveStream->configureMediaObserver(observer, userTag);
|
||||
}
|
||||
|
||||
// Processes incoming data
|
||||
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return;
|
||||
|
||||
if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize))
|
||||
{
|
||||
ICELogMedia(<<"Adding new data to stream processing");
|
||||
mActiveStream->dataArrived(s, dataBuffer, dataSize, source);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
|
||||
{
|
||||
s->sendDatagram(destination, buffer, size);
|
||||
}
|
||||
|
||||
// Create SDP offer
|
||||
void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction)
|
||||
{
|
||||
if (mRemoteNoSdp)
|
||||
return;
|
||||
|
||||
if (mState & (int)StreamState::Srtp)
|
||||
{
|
||||
// Check if SRTP suite is found already or not
|
||||
if (mSrtpSuite == SRTP_NONE)
|
||||
{
|
||||
for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++)
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite)));
|
||||
}
|
||||
else
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||
}
|
||||
|
||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||
if (mAvailableCodecs.empty())
|
||||
{
|
||||
for (int i=0; i<mCodecPriority.count(mTerminal.codeclist()); i++)
|
||||
mCodecPriority.codecAt(mTerminal.codeclist(), i).updateSdp(sdp.codecs(), direction);
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAvailableCodecs.front().mFactory->updateSdp(sdp.codecs(), direction);
|
||||
if (mRemoteTelephoneCodec)
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||
}
|
||||
|
||||
// Publish stream state
|
||||
const char* attr = nullptr;
|
||||
switch (mActive)
|
||||
{
|
||||
case mfActive:
|
||||
switch(mRemoteState)
|
||||
{
|
||||
case msSendonly: attr = "recvonly"; break;
|
||||
case msInactive: attr = "recvonly"; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case mfPaused:
|
||||
switch (mRemoteState)
|
||||
{
|
||||
case msRecvonly: attr = "sendonly"; break;
|
||||
case msSendonly: attr = "inactive"; break;
|
||||
case msInactive: attr = "inactive"; break;
|
||||
case msSendRecv: attr = "sendonly"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (attr)
|
||||
sdp.addAttribute(attr);
|
||||
}
|
||||
|
||||
void AudioProvider::sessionDeleted()
|
||||
{
|
||||
sessionTerminated();
|
||||
}
|
||||
|
||||
void AudioProvider::sessionTerminated()
|
||||
{
|
||||
ICELogDebug(<< "sessionTerminated() for audio provider");
|
||||
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving));
|
||||
|
||||
if (mActiveStream)
|
||||
{
|
||||
ICELogDebug(<< "Copy statistics from existing stream before freeing.");
|
||||
|
||||
// Copy statistics - maybe it will be requested later
|
||||
mBackupStats = mActiveStream->statistics();
|
||||
|
||||
ICELogDebug(<< "Remove stream from terminal");
|
||||
mTerminal.freeStream(mActiveStream);
|
||||
|
||||
// Retrieve final statistics
|
||||
MT::AudioStream* audio_stream = dynamic_cast<MT::AudioStream*>(mActiveStream.get());
|
||||
if (audio_stream)
|
||||
audio_stream->setFinalStatisticsOutput(&mBackupStats);
|
||||
|
||||
ICELogDebug(<< "Reset reference to stream.");
|
||||
mActiveStream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProvider::sessionEstablished(int conntype)
|
||||
{
|
||||
// Start media streams
|
||||
setState(state() | (int)StreamState::Receiving | (int)StreamState::Sending);
|
||||
|
||||
// Available codec list can be empty in case of no-sdp offers.
|
||||
if (conntype == EV_SIP && !mAvailableCodecs.empty() && mActiveStream)
|
||||
{
|
||||
RemoteCodec& rc = mAvailableCodecs.front();
|
||||
mActiveStream->setTransmittingCodec(*rc.mFactory, rc.mRemotePayloadType);
|
||||
dynamic_cast<MT::AudioStream*>(mActiveStream.get())->setTelephoneCodec(mRemoteTelephoneCodec);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProvider::setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6)
|
||||
{
|
||||
mSocket4 = p4;
|
||||
mSocket6 = p6;
|
||||
mActiveStream->setSocket(p4);
|
||||
}
|
||||
|
||||
RtpPair<PDatagramSocket>& AudioProvider::socket(int family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
return mSocket4;
|
||||
|
||||
case AF_INET6:
|
||||
return mSocket6;
|
||||
}
|
||||
return mSocket4;
|
||||
}
|
||||
|
||||
|
||||
bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection)
|
||||
{
|
||||
// Check if there is compatible codec
|
||||
mAvailableCodecs.clear();
|
||||
mRemoteTelephoneCodec = 0;
|
||||
|
||||
// Check if there is SDP at all
|
||||
mRemoteNoSdp = media.codecs().empty();
|
||||
if (mRemoteNoSdp)
|
||||
return true;
|
||||
|
||||
// Update RFC2833 related information
|
||||
findRfc2833(media.codecs());
|
||||
|
||||
// Use CodecListPriority mCodecPriority to work with codec priorities
|
||||
int pt;
|
||||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||
{
|
||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||
|
||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||
}
|
||||
|
||||
if (!mAvailableCodecs.size())
|
||||
return false;
|
||||
|
||||
// Iterate SRTP crypto: attributes
|
||||
if (media.exists("crypto"))
|
||||
{
|
||||
// Find the most strong crypt suite
|
||||
const std::list<resip::Data>& vl = media.getValues("crypto");
|
||||
SrtpSuite ss = SRTP_NONE;
|
||||
ByteBuffer key;
|
||||
for (std::list<resip::Data>::const_iterator attrIter = vl.begin(); attrIter != vl.end(); attrIter++)
|
||||
{
|
||||
const resip::Data& attr = *attrIter;
|
||||
ByteBuffer tempkey;
|
||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey);
|
||||
if (suite > ss)
|
||||
{
|
||||
ss = suite;
|
||||
mSrtpSuite = suite;
|
||||
key = tempkey;
|
||||
}
|
||||
}
|
||||
|
||||
// If SRTP suite is agreed
|
||||
if (ss != SRTP_NONE)
|
||||
{
|
||||
ICELogInfo(<< "Found SRTP suite " << ss);
|
||||
mActiveStream->srtp().open(key, ss);
|
||||
setState(state() | (int)StreamState::Srtp);
|
||||
}
|
||||
else
|
||||
ICELogInfo(<< "Did not find valid SRTP suite");
|
||||
}
|
||||
|
||||
DataProvider::processSdpOffer(media, sdpDirection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioProvider::setState(unsigned state)
|
||||
{
|
||||
mState = state;
|
||||
if (mActiveStream)
|
||||
mActiveStream->setState(state);
|
||||
}
|
||||
|
||||
unsigned AudioProvider::state()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
MT::Statistics AudioProvider::getStatistics()
|
||||
{
|
||||
if (mActiveStream)
|
||||
return mActiveStream->statistics();
|
||||
else
|
||||
return mBackupStats;
|
||||
}
|
||||
|
||||
MT::PStream AudioProvider::activeStream()
|
||||
{
|
||||
return mActiveStream;
|
||||
}
|
||||
|
||||
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return "";
|
||||
|
||||
// Use tag 1 - it is ok, as we use only single crypto attribute
|
||||
int srtpTag = 1;
|
||||
|
||||
// Print key to base64 string
|
||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||
resip::Data keyText = d.base64encode();
|
||||
|
||||
// Create "crypto" attribute value
|
||||
char buffer[512];
|
||||
const char* suiteName = NULL;
|
||||
switch (suite)
|
||||
{
|
||||
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
|
||||
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
|
||||
default: assert(0);
|
||||
}
|
||||
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
|
||||
{
|
||||
int srtpTag = 0;
|
||||
char suite[64], keyChunk[256];
|
||||
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
|
||||
if (components != 3)
|
||||
return SRTP_NONE;
|
||||
|
||||
const char* delimiter = strchr(keyChunk, '|');
|
||||
resip::Data keyText;
|
||||
if (delimiter)
|
||||
keyText = resip::Data(keyChunk, delimiter - keyChunk);
|
||||
else
|
||||
keyText = resip::Data(keyChunk);
|
||||
|
||||
resip::Data rawkey = keyText.base64decode();
|
||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
||||
|
||||
// Open srtp
|
||||
SrtpSuite result = SRTP_NONE;
|
||||
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
|
||||
result = SRTP_AES_128_AUTH_80;
|
||||
else
|
||||
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
|
||||
result = SRTP_AES_256_AUTH_80;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||
{
|
||||
resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter;
|
||||
for (codecIter = codecs.begin(); codecIter != codecs.end(); codecIter++)
|
||||
{
|
||||
if (strcmp("TELEPHONE-EVENT", codecIter->getName().c_str()) == 0 ||
|
||||
strcmp("telephone-event", codecIter->getName().c_str()) == 0)
|
||||
mRemoteTelephoneCodec = codecIter->payloadType();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProvider::readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction)
|
||||
{
|
||||
// Iterate stream list
|
||||
if (mActiveStream)
|
||||
mActiveStream->readFile(stream, direction);
|
||||
}
|
||||
|
||||
void AudioProvider::writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction)
|
||||
{
|
||||
if (mActiveStream)
|
||||
mActiveStream->writeFile(stream, direction);
|
||||
}
|
||||
|
||||
void AudioProvider::setupMirror(bool enable)
|
||||
{
|
||||
if (mActiveStream)
|
||||
mActiveStream->setupMirror(enable);
|
||||
}
|
||||
117
src/engine/endpoint/EP_AudioProvider.h
Normal file
117
src/engine/endpoint/EP_AudioProvider.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __AUDIO_PROVIDER_H
|
||||
#define __AUDIO_PROVIDER_H
|
||||
|
||||
#include "EP_DataProvider.h"
|
||||
#include "../helper/HL_InternetAddress.h"
|
||||
#include "../helper/HL_Rtp.h"
|
||||
#include "../media/MT_Box.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
#include "../media/MT_Codec.h"
|
||||
#include "../audio/Audio_Interface.h"
|
||||
|
||||
#include "rutil/ThreadIf.hxx"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class UserAgent;
|
||||
|
||||
class AudioProvider: public DataProvider
|
||||
{
|
||||
public:
|
||||
|
||||
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
|
||||
virtual ~AudioProvider();
|
||||
|
||||
// Returns provider RTP name
|
||||
std::string streamName();
|
||||
|
||||
// Returns provider RTP profile name
|
||||
std::string streamProfile();
|
||||
|
||||
// Sets destination IP address
|
||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr);
|
||||
|
||||
// Processes incoming data
|
||||
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source);
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
|
||||
|
||||
// Updates SDP offer
|
||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction);
|
||||
|
||||
// Called by user agent when session is deleted.
|
||||
void sessionDeleted();
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
void sessionTerminated();
|
||||
|
||||
// Called by user agent when session is started.
|
||||
void sessionEstablished(int conntype);
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6);
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
RtpPair<PDatagramSocket>& socket(int family);
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
// myAnswer sets if the answer will be sent after.
|
||||
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection);
|
||||
|
||||
void setState(unsigned state);
|
||||
unsigned state();
|
||||
MT::Statistics getStatistics();
|
||||
MT::PStream activeStream();
|
||||
|
||||
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
|
||||
void writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction);
|
||||
void setupMirror(bool enable);
|
||||
|
||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
|
||||
// Socket handles to operate
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
|
||||
// Destination IP4/6 address
|
||||
RtpPair<InternetAddress> mDestination;
|
||||
|
||||
MT::PStream mActiveStream;
|
||||
UserAgent& mUserAgent;
|
||||
MT::Terminal& mTerminal;
|
||||
MT::Statistics mBackupStats;
|
||||
|
||||
unsigned mState;
|
||||
SrtpSuite mSrtpSuite;
|
||||
struct RemoteCodec
|
||||
{
|
||||
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
|
||||
:mFactory(factory), mRemotePayloadType(payloadType)
|
||||
{ }
|
||||
|
||||
MT::Codec::Factory* mFactory;
|
||||
int mRemotePayloadType;
|
||||
};
|
||||
std::vector<RemoteCodec> mAvailableCodecs;
|
||||
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
|
||||
bool mRemoteNoSdp; // Marks if we got no-sdp offer
|
||||
MT::CodecListPriority mCodecPriority;
|
||||
MT::Stream::MediaObserver* mMediaObserver = nullptr;
|
||||
void* mMediaObserverTag = nullptr;
|
||||
|
||||
std::string createCryptoAttribute(SrtpSuite suite);
|
||||
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||
};
|
||||
|
||||
#endif
|
||||
74
src/engine/endpoint/EP_DataProvider.cpp
Normal file
74
src/engine/endpoint/EP_DataProvider.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EP_DataProvider.h"
|
||||
#include "../helper/HL_StreamState.h"
|
||||
|
||||
bool DataProvider::isSupported(const char* name)
|
||||
{
|
||||
return !strcmp(name, "audio");
|
||||
|
||||
//return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
|
||||
}
|
||||
|
||||
void DataProvider::pause()
|
||||
{
|
||||
/*if (state() & STATE_SIPRECV)
|
||||
setState( state() & ~STATE_SIPRECV );*/
|
||||
|
||||
// Stop receive RTP stream
|
||||
if (state() & (int)StreamState::Receiving)
|
||||
setState( state() & ~(int)StreamState::Receiving );
|
||||
|
||||
mActive = mfPaused;
|
||||
}
|
||||
|
||||
void DataProvider::resume()
|
||||
{
|
||||
// Tell remote peer about resumed receiving in SDP
|
||||
//setState( state() | STATE_SIPRECV );
|
||||
|
||||
// Start receive RTP stream
|
||||
setState( state() | (int)StreamState::Receiving );
|
||||
|
||||
mActive = mfActive;
|
||||
}
|
||||
|
||||
bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection)
|
||||
{
|
||||
// Process paused and inactive calls
|
||||
if (media.exists("sendonly"))
|
||||
{
|
||||
mRemoteState = msSendonly;
|
||||
setState(state() & ~(int)StreamState::Sending);
|
||||
}
|
||||
else
|
||||
if (media.exists("recvonly"))
|
||||
{
|
||||
mRemoteState = msRecvonly;
|
||||
setState(state() & ~(int)StreamState::Receiving);
|
||||
}
|
||||
else
|
||||
if (media.exists("inactive"))
|
||||
{
|
||||
mRemoteState = msInactive;
|
||||
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mRemoteState = msSendRecv;
|
||||
switch (mActive)
|
||||
{
|
||||
case mfActive:
|
||||
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
|
||||
break;
|
||||
|
||||
case mfPaused:
|
||||
setState(state() | (int)StreamState::Sending );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
92
src/engine/endpoint/EP_DataProvider.h
Normal file
92
src/engine/endpoint/EP_DataProvider.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __DATA_PROVIDER_H
|
||||
#define __DATA_PROVIDER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "resip/stack/SdpContents.hxx"
|
||||
#include "rutil/SharedPtr.hxx"
|
||||
|
||||
#include "../helper/HL_InternetAddress.h"
|
||||
#include "../helper/HL_NetworkSocket.h"
|
||||
#include "../helper/HL_Pointer.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
|
||||
class DataProvider
|
||||
{
|
||||
public:
|
||||
enum MediaFlow
|
||||
{
|
||||
mfActive,
|
||||
mfPaused
|
||||
};
|
||||
|
||||
enum MediaState
|
||||
{
|
||||
msSendRecv,
|
||||
msSendonly,
|
||||
msRecvonly,
|
||||
msInactive
|
||||
};
|
||||
|
||||
static bool isSupported(const char* name);
|
||||
|
||||
// Returns provider RTP name
|
||||
virtual std::string streamName() = 0;
|
||||
|
||||
// Returns provider RTP profile name
|
||||
virtual std::string streamProfile() = 0;
|
||||
|
||||
// Sets destination IP address
|
||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||
|
||||
// Processes incoming data
|
||||
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||
|
||||
// Updates SDP offer
|
||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
||||
|
||||
// Called by user agent when session is deleted. Comes after sessionTerminated().
|
||||
virtual void sessionDeleted() = 0;
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
virtual void sessionTerminated() = 0;
|
||||
|
||||
// Called by user agent when session is started.
|
||||
virtual void sessionEstablished(int conntype) = 0;
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
virtual void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) = 0;
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
virtual RtpPair<PDatagramSocket>& socket(int family) = 0;
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
|
||||
|
||||
virtual unsigned state() = 0;
|
||||
virtual void setState(unsigned state) = 0;
|
||||
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
|
||||
virtual MT::Statistics getStatistics() = 0;
|
||||
|
||||
protected:
|
||||
MediaFlow mActive;
|
||||
MediaState mRemoteState;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DataProvider> PDataProvider;
|
||||
typedef std::vector<PDataProvider> DataProviderVector;
|
||||
|
||||
#endif
|
||||
1612
src/engine/endpoint/EP_Engine.cpp
Normal file
1612
src/engine/endpoint/EP_Engine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
484
src/engine/endpoint/EP_Engine.h
Normal file
484
src/engine/endpoint/EP_Engine.h
Normal file
@@ -0,0 +1,484 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ENGINE_H
|
||||
#define __ENGINE_H
|
||||
|
||||
#include "resip/stack/SdpContents.hxx"
|
||||
#include "resip/stack/SipMessage.hxx"
|
||||
#include "resip/stack/ShutdownMessage.hxx"
|
||||
#include "resip/stack/SipStack.hxx"
|
||||
#include "resip/stack/InternalTransport.hxx"
|
||||
#include "resip/dum/ClientAuthManager.hxx"
|
||||
#include "resip/dum/ClientInviteSession.hxx"
|
||||
#include "resip/dum/ClientRegistration.hxx"
|
||||
#include "resip/dum/DialogUsageManager.hxx"
|
||||
#include "resip/dum/DumShutdownHandler.hxx"
|
||||
#include "resip/dum/InviteSessionHandler.hxx"
|
||||
#include "resip/dum/MasterProfile.hxx"
|
||||
#include "resip/dum/RegistrationHandler.hxx"
|
||||
#include "resip/dum/ServerInviteSession.hxx"
|
||||
#include "resip/dum/ServerOutOfDialogReq.hxx"
|
||||
#include "resip/dum/OutOfDialogHandler.hxx"
|
||||
#include "resip/dum/AppDialog.hxx"
|
||||
#include "resip/dum/AppDialogSet.hxx"
|
||||
#include "resip/dum/AppDialogSetFactory.hxx"
|
||||
#include "resip/dum/ClientPublication.hxx"
|
||||
#include "resip/dum/ClientSubscription.hxx"
|
||||
#include "resip/dum/SubscriptionHandler.hxx"
|
||||
#include "resip/dum/PagerMessageHandler.hxx"
|
||||
#include "resip/dum/PublicationHandler.hxx"
|
||||
#include "resip/dum/ClientPagerMessage.hxx"
|
||||
#include "resip/dum/ServerPagerMessage.hxx"
|
||||
|
||||
#include "rutil/Log.hxx"
|
||||
#include "rutil/Logger.hxx"
|
||||
#include "rutil/Random.hxx"
|
||||
#include "rutil/WinLeakCheck.hxx"
|
||||
#include "rutil/DnsUtil.hxx"
|
||||
#include "resip/stack/DnsResult.hxx"
|
||||
#include "resip/stack/SipStack.hxx"
|
||||
#include "rutil/dns/RRVip.hxx"
|
||||
#include "rutil/dns/QueryTypes.hxx"
|
||||
#include "rutil/dns/DnsStub.hxx"
|
||||
#include "../ice/ICEBox.h"
|
||||
#include "../ice/ICETime.h"
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
#include "../config.h"
|
||||
#include "EP_Session.h"
|
||||
#include "EP_Observer.h"
|
||||
#include "EP_DataProvider.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "../helper/HL_SocketHeap.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
|
||||
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
|
||||
|
||||
using namespace std;
|
||||
enum
|
||||
{
|
||||
CONFIG_IPV4 = 0, // Use IP4
|
||||
CONFIG_IPV6, // Use IP6.
|
||||
CONFIG_USERNAME, // Username. String value.
|
||||
CONFIG_DOMAIN, // Domain. String value.
|
||||
CONFIG_PASSWORD, // Password. String value.
|
||||
CONFIG_RINSTANCE, // Determines if SIP rinstance field has to be used during registration. Boolean value.
|
||||
CONFIG_INSTANCE_ID, // Instance id. It is alternative option to rinstance.
|
||||
CONFIG_DISPLAYNAME, // Optional user display name. String value.
|
||||
CONFIG_DOMAINPORT, // Optional domain port number. Integer value.
|
||||
CONFIG_REGISTERDURATION, // Wanted duration for registration. Integer value. It is MANDATORY value.
|
||||
CONFIG_RPORT, // Use SIP rport field. Recommended to set it to true. Boolean value.
|
||||
CONFIG_KEEPALIVETIME, // Interval between UDP keep-alive messages. Boolean value.
|
||||
CONFIG_RELAY, // Sets if TURN server must be used instead of STUN. Boolean value.
|
||||
CONFIG_ICETIMEOUT, // Optional timeout for ICE connectivity checks and candidate gathering. Integer value.
|
||||
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
|
||||
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
|
||||
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
|
||||
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
|
||||
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
|
||||
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
|
||||
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
|
||||
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
|
||||
CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS,
|
||||
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds)
|
||||
CONFIG_SUBSCRIPTION_REFRESHTIME, // Refresh interval for subscriptions
|
||||
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
|
||||
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
|
||||
CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
|
||||
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction
|
||||
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
|
||||
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
|
||||
CONFIG_DEFERRELAYED, // Defer relayed media path
|
||||
CONFIG_PROXY, // Proxy host name or IP address
|
||||
CONFIG_PROXYPORT, // Proxy port number
|
||||
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
|
||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||
CONFIG_REGID // reg-id value from RFC5626
|
||||
};
|
||||
|
||||
// Conntype parameter for OnSessionEstablished event
|
||||
enum
|
||||
{
|
||||
EV_SIP = 1,
|
||||
EV_ICE = 2
|
||||
};
|
||||
|
||||
class UserAgent;
|
||||
|
||||
// Define a type for asynchronous requests to user agent
|
||||
class SIPAction
|
||||
{
|
||||
public:
|
||||
virtual void Run(UserAgent& ua) = 0;
|
||||
};
|
||||
|
||||
typedef std::vector<SIPAction*> SIPActionVector;
|
||||
|
||||
// Session termination reason
|
||||
enum
|
||||
{
|
||||
Error,
|
||||
Timeout,
|
||||
Replaced,
|
||||
LocalBye,
|
||||
RemoteBye,
|
||||
LocalCancel,
|
||||
RemoteCancel,
|
||||
Rejected, //Only as UAS, UAC has distinct onFailure callback
|
||||
Referred
|
||||
};
|
||||
|
||||
class UserAgent: public resip::ClientRegistrationHandler,
|
||||
public resip::InviteSessionHandler,
|
||||
public resip::DumShutdownHandler,
|
||||
public resip::ExternalLogger,
|
||||
public resip::DnsResultSink,
|
||||
public resip::ClientSubscriptionHandler,
|
||||
public resip::ServerSubscriptionHandler,
|
||||
public resip::ClientPagerMessageHandler,
|
||||
public resip::ServerPagerMessageHandler,
|
||||
public resip::ClientPublicationHandler,
|
||||
public resip::InternalTransport::TransportLogger
|
||||
{
|
||||
friend class Account;
|
||||
friend class Session;
|
||||
friend class ResipSession;
|
||||
friend class NATDecorator;
|
||||
friend class WatcherQueue;
|
||||
public:
|
||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||
static bool compareSipAddresses(std::string sip1, std::string sip2);
|
||||
static std::string formatSipAddress(std::string sip);
|
||||
static bool isSipAddressValid(std::string sip);
|
||||
struct SipAddress
|
||||
{
|
||||
bool mValid;
|
||||
std::string mScheme;
|
||||
std::string mUsername;
|
||||
std::string mDomain;
|
||||
std::string mDisplayname;
|
||||
};
|
||||
|
||||
static SipAddress parseSipAddress(const std::string& sip);
|
||||
|
||||
UserAgent();
|
||||
virtual ~UserAgent();
|
||||
|
||||
/* Brings user agent online. Basically it creates a signalling socket(s).
|
||||
This is asynchronous method. */
|
||||
void start();
|
||||
|
||||
/* Shutdowns user agent. It closes all sessions, tries to unregister from server and disconnects from it.
|
||||
This is asynchronous method. onStop() event will be called later */
|
||||
void shutdown();
|
||||
|
||||
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */
|
||||
void stop();
|
||||
|
||||
/* Checks if user agent is active (started). */
|
||||
bool active();
|
||||
|
||||
/* Used to refresh existing registration(s), publication, subscriptions. */
|
||||
void refresh();
|
||||
|
||||
/* Runs sip & ice stacks. Event handlers are called in its context. */
|
||||
void process();
|
||||
|
||||
/* Adds root cert in PEM format. Usable after start() call. */
|
||||
void addRootCert(const ByteBuffer& data);
|
||||
|
||||
PAccount createAccount(PVariantMap config);
|
||||
void deleteAccount(PAccount account);
|
||||
|
||||
/* Creates session. Returns session ID. */
|
||||
PSession createSession(PAccount account);
|
||||
|
||||
// Must be called when IP interface list is changed
|
||||
void updateInterfaceList();
|
||||
|
||||
// Called on new incoming session; providers shoukld
|
||||
virtual PDataProvider onProviderNeeded(const std::string& name) = 0;
|
||||
|
||||
// Called on new session offer
|
||||
virtual void onNewSession(PSession s) = 0;
|
||||
|
||||
// Called when session is terminated
|
||||
virtual void onSessionTerminated(PSession s, int responsecode, int reason) = 0;
|
||||
|
||||
// Called when session is established ok i.e. after all ICE signalling is finished
|
||||
// Conntype is type of establish event - EV_SIP or EV_ICE
|
||||
virtual void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) = 0;
|
||||
|
||||
// Called when client session gets
|
||||
virtual void onSessionProvisional(PSession s, int code) = 0;
|
||||
|
||||
// Called when user agent started
|
||||
virtual void onStart(int errorcode) = 0;
|
||||
|
||||
// Called when user agent stopped
|
||||
virtual void onStop() = 0;
|
||||
|
||||
// Called when account registered
|
||||
virtual void onAccountStart(PAccount account) = 0;
|
||||
|
||||
// Called when account removed or failed (non zero error code)
|
||||
virtual void onAccountStop(PAccount account, int error) = 0;
|
||||
|
||||
// Called when connectivity checks failed.
|
||||
virtual void onConnectivityFailed(PSession s) = 0;
|
||||
|
||||
// Called when new candidate is gathered
|
||||
virtual void onCandidateGathered(PSession s, const char* address);
|
||||
|
||||
// Called when network change detected
|
||||
virtual void onNetworkChange(PSession s) = 0;
|
||||
|
||||
// Called when all candidates are gathered
|
||||
virtual void onGathered(PSession s);
|
||||
|
||||
// Called when new connectivity check is finished
|
||||
virtual void onCheckFinished(PSession s, const char* description);
|
||||
|
||||
// Called when log message must be recorded
|
||||
virtual void onLog(const char* msg);
|
||||
|
||||
// Called when problem with SIP connection(s) detected
|
||||
virtual void onSipConnectionFailed() = 0;
|
||||
|
||||
// Subscribe/publish presence methods
|
||||
virtual void onPublicationSuccess(PAccount acc);
|
||||
virtual void onPublicationTerminated(PAccount acc, int code);
|
||||
virtual void onClientObserverStart(PClientObserver observer);
|
||||
virtual void onServerObserverStart(PServerObserver observer);
|
||||
virtual void onClientObserverStop(PClientObserver observer, int code);
|
||||
virtual void onServerObserverStop(PServerObserver observer, int code);
|
||||
|
||||
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content);
|
||||
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
|
||||
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
|
||||
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
|
||||
|
||||
// Configuration methods
|
||||
VariantMap& config();
|
||||
|
||||
public:
|
||||
// InviteSessionHandler implementation
|
||||
#pragma region InviteSessionHandler implementation
|
||||
/// called when an initial INVITE or the intial response to an outoing invite
|
||||
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
|
||||
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
|
||||
|
||||
/// Received a failure response from UAS
|
||||
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when an in-dialog provisional response is received that contains an SDP body
|
||||
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override;
|
||||
|
||||
/// called when dialog enters the Early state - typically after getting 18x
|
||||
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
|
||||
|
||||
/// called when a dialog initiated as a UAC enters the connected state
|
||||
virtual void onConnected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when a dialog initiated as a UAS enters the connected state
|
||||
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override;
|
||||
|
||||
/// called when a fork that was created through a 1xx never receives a 2xx
|
||||
/// because another fork answered and this fork was canceled by a proxy.
|
||||
virtual void onForkDestroyed(resip::ClientInviteSessionHandle) override;
|
||||
|
||||
/// called when a 3xx with valid targets is encountered in an early dialog
|
||||
/// This is different then getting a 3xx in onTerminated, as another
|
||||
/// request will be attempted, so the DialogSet will not be destroyed.
|
||||
/// Basically an onTermintated that conveys more information.
|
||||
/// checking for 3xx respones in onTerminated will not work as there may
|
||||
/// be no valid targets.
|
||||
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when an SDP answer is received - has nothing to do with user
|
||||
/// answering the call
|
||||
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
|
||||
|
||||
/// called when an SDP offer is received - must send an answer soon after this
|
||||
virtual void onOffer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
|
||||
|
||||
/// called when an Invite w/out SDP is sent, or any other context which
|
||||
/// requires an SDP offer from the user
|
||||
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
|
||||
/// useful. A SipMessage is provided if one is available
|
||||
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg) override;
|
||||
|
||||
/// called when INFO message is received
|
||||
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when response to INFO message is received
|
||||
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when MESSAGE message is received
|
||||
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when response to MESSAGE message is received
|
||||
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when an REFER message is received. The refer is accepted or
|
||||
/// rejected using the server subscription. If the offer is accepted,
|
||||
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
||||
/// InviteSession that will send notify messages using the ServerSubscription
|
||||
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when an REFER message receives a failure response
|
||||
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// called when an REFER message receives an accepted response
|
||||
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
|
||||
#pragma endregion
|
||||
|
||||
// ClientRegistrationHandler implementation
|
||||
#pragma region ClientRegistrationHandler implementation
|
||||
/// Called when registraion succeeds or each time it is sucessfully
|
||||
/// refreshed.
|
||||
void onSuccess(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
|
||||
|
||||
// Called when all of my bindings have been removed
|
||||
void onRemoved(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
|
||||
|
||||
/// call on Retry-After failure.
|
||||
/// return values: -1 = fail, 0 = retry immediately, N = retry in N seconds
|
||||
int onRequestRetry(resip::ClientRegistrationHandle, int retrySeconds, const resip::SipMessage& response) override;
|
||||
|
||||
/// Called if registration fails, usage will be destroyed (unless a
|
||||
/// Registration retry interval is enabled in the Profile)
|
||||
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ExternalLogger implementation
|
||||
/** return true to also do default logging, false to suppress default logging. */
|
||||
virtual bool operator()(resip::Log::Level level,
|
||||
const resip::Subsystem& subsystem,
|
||||
const resip::Data& appName,
|
||||
const char* file,
|
||||
int line,
|
||||
const resip::Data& message,
|
||||
const resip::Data& messageWithHeaders) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DnsResultSink implementation
|
||||
|
||||
virtual void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&) override;
|
||||
virtual void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) override;
|
||||
virtual void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&) override;
|
||||
virtual void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) override;
|
||||
virtual void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region TransportLogger implementation
|
||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ClientPublicationHandler
|
||||
void onSuccess(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
|
||||
void onRemove(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
|
||||
void onFailure(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
|
||||
int onRequestRetry(resip::ClientPublicationHandle, int retrySeconds, const resip::SipMessage& status) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region SubscriptionHandler
|
||||
void onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify);
|
||||
void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
|
||||
void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
|
||||
|
||||
//unknown Subscription-State value
|
||||
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
|
||||
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
|
||||
|
||||
//subscription can be ended through a notify or a failure response.
|
||||
void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override;
|
||||
//not sure if this has any value.
|
||||
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
|
||||
|
||||
/// called to allow app to adorn a message.
|
||||
void onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg) override;
|
||||
void onNotifyNotReceived(resip::ClientSubscriptionHandle) override;
|
||||
|
||||
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
|
||||
/// errors, or missing CRLF keep alives pong responses from the server.
|
||||
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
|
||||
/// supports RFC5626 (outbound).
|
||||
/// Default implementation is to re-form the subscription using a new flow
|
||||
void onFlowTerminated(resip::ClientSubscriptionHandle) override;
|
||||
void onNewSubscription(resip::ServerSubscriptionHandle, const resip::SipMessage& sub) override;
|
||||
void onTerminated(resip::ServerSubscriptionHandle) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region PagerHandler
|
||||
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
|
||||
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::auto_ptr<resip::Contents> contents) override;
|
||||
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
|
||||
#pragma endregion
|
||||
|
||||
void onDumCanBeDeleted() override;
|
||||
protected:
|
||||
// Mutex to protect this instance
|
||||
Mutex mGuard;
|
||||
|
||||
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
||||
resip::SharedPtr<resip::MasterProfile> mProfile;
|
||||
|
||||
// Resiprocate's SIP stack object pointer
|
||||
resip::SipStack* mStack;
|
||||
|
||||
// Resiprocate's dialog usage manager object pointer
|
||||
resip::DialogUsageManager* mDum;
|
||||
|
||||
// List of available transports. They are owned by SipStack - so there is no need to delete instances in UserAgent.
|
||||
std::vector<resip::InternalTransport*> mTransportList;
|
||||
|
||||
typedef std::map<int, PSession> SessionMap;
|
||||
|
||||
// Session's map
|
||||
SessionMap mSessionMap;
|
||||
|
||||
// Used configuration
|
||||
VariantMap mConfig;
|
||||
|
||||
// Action vector
|
||||
SIPActionVector mActionVector;
|
||||
|
||||
typedef std::map<int, PClientObserver> ClientObserverMap;
|
||||
ClientObserverMap mClientObserverMap;
|
||||
|
||||
typedef std::map<int, PServerObserver> ServerObserverMap;
|
||||
ServerObserverMap mServerObserverMap;
|
||||
|
||||
typedef std::set<PAccount> AccountSet;
|
||||
AccountSet mAccountSet;
|
||||
|
||||
// Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object.
|
||||
void sendOffer(Session* session);
|
||||
void internalStopSession(Session& session);
|
||||
void processWatchingList();
|
||||
bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
|
||||
|
||||
PSession getUserSession(int sessionId);
|
||||
PAccount getAccount(const resip::NameAddr& myAddr);
|
||||
PAccount getAccount(Account* account);
|
||||
PAccount getAccount(int sessionId);
|
||||
};
|
||||
#endif
|
||||
182
src/engine/endpoint/EP_NetworkQueue.cpp
Normal file
182
src/engine/endpoint/EP_NetworkQueue.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "EP_NetworkQueue.h"
|
||||
#include "EP_Engine.h"
|
||||
|
||||
WatcherQueue::WatcherQueue(UserAgent& ua)
|
||||
:mActiveId(0), mAgent(ua)
|
||||
{}
|
||||
|
||||
WatcherQueue::~WatcherQueue()
|
||||
{}
|
||||
|
||||
int WatcherQueue::add(std::string peer, std::string package, void* tag)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// Check if queue has similar item
|
||||
for (unsigned i=0; i<mItemList.size(); i++)
|
||||
{
|
||||
Item& item = mItemList[i];
|
||||
if (item.mTarget == peer && item.mPackage == package &&
|
||||
item.mState != Item::State_Deleting)
|
||||
return item.mId;
|
||||
}
|
||||
|
||||
Item item;
|
||||
item.mTarget = peer;
|
||||
item.mPackage = package;
|
||||
item.mTag = tag;
|
||||
item.mState = Item::State_ScheduledToAdd;
|
||||
item.mSession = new ResipSession(*mAgent.mDum);
|
||||
item.mSession->setUa(&mAgent);
|
||||
item.mSession->setType(ResipSession::Type_Subscription);
|
||||
item.mSession->setTag(tag);
|
||||
item.mId = item.mSession->sessionId();
|
||||
item.mSession->setRemoteAddress(peer);
|
||||
item.mTag = tag;
|
||||
mItemList.push_back(item);
|
||||
process();
|
||||
|
||||
return item.mId;
|
||||
}
|
||||
|
||||
void WatcherQueue::remove(int id)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// Check if queue has similar item
|
||||
for (unsigned i=0; i<mItemList.size(); i++)
|
||||
{
|
||||
Item& item = mItemList[i];
|
||||
if (item.mId == id && !id)
|
||||
{
|
||||
if (item.mState != Item::State_Deleting)
|
||||
item.mState = Item::State_ScheduledToDelete;
|
||||
}
|
||||
}
|
||||
process();
|
||||
}
|
||||
|
||||
|
||||
void WatcherQueue::refresh(int id)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// Check if queue has similar item
|
||||
for (unsigned i=0; i<mItemList.size(); i++)
|
||||
{
|
||||
Item& item = mItemList[i];
|
||||
if (item.mId == id && !id)
|
||||
{
|
||||
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
|
||||
item.mState = Item::State_ScheduledToRefresh;
|
||||
}
|
||||
}
|
||||
process();
|
||||
}
|
||||
|
||||
void WatcherQueue::process()
|
||||
{
|
||||
while (!mActiveId)
|
||||
{
|
||||
// Find next item to process
|
||||
ItemList::iterator i = mItemList.begin();
|
||||
for (;i != mItemList.end() && !i->scheduled(); i++)
|
||||
;
|
||||
if (i == mItemList.end())
|
||||
return;
|
||||
|
||||
resip::SharedPtr<resip::SipMessage> msg;
|
||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||
|
||||
switch (i->mState)
|
||||
{
|
||||
case Item::State_ScheduledToAdd:
|
||||
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_TIME))
|
||||
expires = mAgent.mConfig[CONFIG_SUBSCRIPTION_TIME].asInt();
|
||||
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
|
||||
refresh = mAgent.mConfig[CONFIG_SUBSCRIPTION_REFRESHTIME].asInt();
|
||||
|
||||
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(i->mTarget)), resip::Data(i->mPackage),
|
||||
expires, refresh, i->mSession);
|
||||
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
|
||||
mActiveId = i->mId;
|
||||
i->mState = Item::State_Adding;
|
||||
mAgent.mDum->send(msg);
|
||||
break;
|
||||
|
||||
case Item::State_ScheduledToDelete:
|
||||
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, 0, 0);
|
||||
if (i->mHandle.isValid())
|
||||
{
|
||||
mActiveId = i->mId;
|
||||
i->mHandle->end();
|
||||
i->mState = Item::State_Deleting;
|
||||
break;
|
||||
}
|
||||
else
|
||||
mItemList.erase(i);
|
||||
break;
|
||||
|
||||
case Item::State_ScheduledToRefresh:
|
||||
if (i->mHandle.isValid())
|
||||
{
|
||||
mActiveId = i->mId;
|
||||
i->mState = Item::State_Refreshing;
|
||||
i->mHandle->requestRefresh();
|
||||
}
|
||||
else
|
||||
mItemList.erase(i);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WatcherQueue::onTerminated(int id, int code)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
ItemList::iterator i = findById(id);
|
||||
if (i != mItemList.end())
|
||||
{
|
||||
if (i->mSession)
|
||||
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
|
||||
mItemList.erase(i);
|
||||
if (i->mId == mActiveId)
|
||||
mActiveId = 0;
|
||||
}
|
||||
process();
|
||||
}
|
||||
|
||||
void WatcherQueue::onEstablished(int id, int code)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
ItemList::iterator i = findById(id);
|
||||
if (i != mItemList.end())
|
||||
{
|
||||
i->mState = Item::State_Active;
|
||||
if (i->mId == mActiveId)
|
||||
mActiveId = 0;
|
||||
}
|
||||
process();
|
||||
}
|
||||
|
||||
WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
|
||||
{
|
||||
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
|
||||
if (i->mId == id)
|
||||
return i;
|
||||
return mItemList.end();
|
||||
}
|
||||
|
||||
void WatcherQueue::clear()
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
|
||||
{
|
||||
if (i->mHandle.isValid())
|
||||
i->mHandle->end();
|
||||
}
|
||||
mItemList.clear();
|
||||
}
|
||||
69
src/engine/endpoint/EP_NetworkQueue.h
Normal file
69
src/engine/endpoint/EP_NetworkQueue.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __NETWORK_QUEUE_H
|
||||
#define __NETWORK_QUEUE_H
|
||||
|
||||
#include "EP_Session.h"
|
||||
#include <resip/dum/ClientSubscription.hxx>
|
||||
|
||||
class UserAgent;
|
||||
class WatcherQueue
|
||||
{
|
||||
public:
|
||||
struct Item
|
||||
{
|
||||
enum State
|
||||
{
|
||||
State_None,
|
||||
State_Active,
|
||||
State_ScheduledToAdd,
|
||||
State_Adding,
|
||||
State_ScheduledToRefresh,
|
||||
State_Refreshing,
|
||||
State_ScheduledToDelete,
|
||||
State_Deleting
|
||||
};
|
||||
|
||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||
ResipSession* mSession;
|
||||
State mState;
|
||||
std::string mTarget; // Target's address
|
||||
std::string mPackage; // Event package
|
||||
void* mTag; // User tag
|
||||
int mId;
|
||||
|
||||
Item()
|
||||
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
|
||||
{}
|
||||
|
||||
bool scheduled()
|
||||
{
|
||||
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
|
||||
}
|
||||
};
|
||||
WatcherQueue(UserAgent& agent);
|
||||
~WatcherQueue();
|
||||
|
||||
int add(std::string peer, std::string package, void* tag);
|
||||
void remove(int id);
|
||||
void refresh(int id);
|
||||
void clear();
|
||||
|
||||
void onTerminated(int id, int code);
|
||||
void onEstablished(int id, int code);
|
||||
|
||||
protected:
|
||||
typedef std::vector<Item> ItemList;
|
||||
ItemList mItemList;
|
||||
ice::Mutex mGuard;
|
||||
UserAgent& mAgent;
|
||||
int mActiveId;
|
||||
|
||||
void process();
|
||||
ItemList::iterator findById(int id);
|
||||
};
|
||||
|
||||
#endif
|
||||
106
src/engine/endpoint/EP_Observer.cpp
Normal file
106
src/engine/endpoint/EP_Observer.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EP_Observer.h"
|
||||
#include "EP_Session.h"
|
||||
|
||||
#include <resip/stack/Pidf.hxx>
|
||||
#include <resip/dum/ClientSubscription.hxx>
|
||||
|
||||
ClientObserver::ClientObserver()
|
||||
{
|
||||
}
|
||||
|
||||
ClientObserver::~ClientObserver()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientObserver::refresh()
|
||||
{
|
||||
if (mHandle.isValid())
|
||||
mHandle->requestRefresh();
|
||||
}
|
||||
|
||||
void ClientObserver::stop()
|
||||
{
|
||||
if (mHandle.isValid())
|
||||
mHandle->end();
|
||||
else
|
||||
if (mSession)
|
||||
{
|
||||
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
|
||||
if (mSession)
|
||||
mSession->end();
|
||||
}
|
||||
mSession = NULL;
|
||||
}
|
||||
|
||||
std::string ClientObserver::peer()
|
||||
{
|
||||
return mPeer;
|
||||
}
|
||||
|
||||
ServerObserver::ServerObserver()
|
||||
:mState(State_Incoming)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ServerObserver::~ServerObserver()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
std::string ServerObserver::peer() const
|
||||
{
|
||||
return mPeer;
|
||||
}
|
||||
|
||||
std::string ServerObserver::package() const
|
||||
{
|
||||
return mPackage;
|
||||
}
|
||||
|
||||
void ServerObserver::update(std::string simpleId, bool online, std::string msg)
|
||||
{
|
||||
if (mState != State_Active)
|
||||
return;
|
||||
|
||||
resip::Pidf p;
|
||||
p.setEntity(mContact);
|
||||
p.setSimpleId(resip::Data(simpleId));
|
||||
p.setSimpleStatus(online, resip::Data(msg));
|
||||
|
||||
if (mHandle.isValid())
|
||||
mHandle->send(mHandle->update(&p));
|
||||
}
|
||||
|
||||
void ServerObserver::accept()
|
||||
{
|
||||
if (mHandle.isValid() && mState == State_Incoming)
|
||||
{
|
||||
mState = State_Active;
|
||||
mHandle->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ServerObserver::stop()
|
||||
{
|
||||
if (!mHandle.isValid())
|
||||
return;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case State_Incoming:
|
||||
mHandle->reject(404);
|
||||
break;
|
||||
case State_Active:
|
||||
mHandle->end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mState = State_Closed;
|
||||
}
|
||||
73
src/engine/endpoint/EP_Observer.h
Normal file
73
src/engine/endpoint/EP_Observer.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef EP_OBSERVER_H
|
||||
#define EP_OBSERVER_H
|
||||
|
||||
#include "../helper/HL_Pointer.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "ice/ICEAddress.h"
|
||||
#include "ice/ICETime.h"
|
||||
#include "resip/dum/UserProfile.hxx"
|
||||
#include "resip/dum/ClientRegistration.hxx"
|
||||
#include "resip/dum/ClientPublication.hxx"
|
||||
#include "resip/stack/DnsInterface.hxx"
|
||||
|
||||
class UserAgent;
|
||||
class Session;
|
||||
class ResipSession;
|
||||
|
||||
class ClientObserver
|
||||
{
|
||||
friend class Account;
|
||||
friend class UserAgent;
|
||||
public:
|
||||
ClientObserver();
|
||||
~ClientObserver();
|
||||
|
||||
void refresh();
|
||||
void stop();
|
||||
std::string peer();
|
||||
|
||||
protected:
|
||||
resip::ClientSubscriptionHandle mHandle;
|
||||
ResipSession* mSession;
|
||||
int mSessionId;
|
||||
std::string mPeer;
|
||||
};
|
||||
|
||||
typedef SharedPtr<ClientObserver> PClientObserver;
|
||||
|
||||
class ServerObserver
|
||||
{
|
||||
friend class UserAgent;
|
||||
public:
|
||||
ServerObserver();
|
||||
~ServerObserver();
|
||||
|
||||
std::string peer() const;
|
||||
std::string package() const;
|
||||
|
||||
void accept();
|
||||
void update(std::string simpleId, bool online, std::string msg);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
enum State
|
||||
{
|
||||
State_Incoming,
|
||||
State_Active,
|
||||
State_Closed
|
||||
};
|
||||
State mState;
|
||||
resip::ServerSubscriptionHandle mHandle;
|
||||
std::string mPeer, mPackage;
|
||||
resip::Uri mContact;
|
||||
int mSessionId;
|
||||
};
|
||||
|
||||
typedef SharedPtr<ServerObserver> PServerObserver;
|
||||
|
||||
#endif // EP_OBSERVER_H
|
||||
304
src/engine/endpoint/EP_ReliableTunnel.cpp
Normal file
304
src/engine/endpoint/EP_ReliableTunnel.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2012 Dmytro Bogovych <dmytro.bogovych@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "EP_ReliableTunnel.h"
|
||||
#include "EP_Engine.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "../ICE/ICECRC32.h"
|
||||
|
||||
enum
|
||||
{
|
||||
CONFIRMATION_PT = 1,
|
||||
DATA_PT = 2
|
||||
};
|
||||
|
||||
#define CONFIRMATION_TIMEOUT 500
|
||||
#define LOG_SUBSYSTEM "RT"
|
||||
|
||||
ReliableTunnel::ReliableTunnel(const char* streamname)
|
||||
{
|
||||
mStack.setEncryption(this);
|
||||
mStreamName = streamname;
|
||||
mBandwidth = 0;
|
||||
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
}
|
||||
|
||||
ReliableTunnel::~ReliableTunnel()
|
||||
{
|
||||
::CloseHandle(mDataSignal);
|
||||
::CloseHandle(mExitSignal);
|
||||
}
|
||||
|
||||
std::string ReliableTunnel::streamName()
|
||||
{
|
||||
return mStreamName;
|
||||
}
|
||||
|
||||
std::string ReliableTunnel::streamProfile()
|
||||
{
|
||||
return "RTP/DP";
|
||||
}
|
||||
|
||||
void ReliableTunnel::setDestinationAddress(InternetAddress& addr)
|
||||
{
|
||||
mDestination = addr;
|
||||
}
|
||||
|
||||
|
||||
void ReliableTunnel::queueData(const void* bufferptr, int buffersize)
|
||||
{
|
||||
assert(bufferptr != NULL);
|
||||
assert(buffersize != 0);
|
||||
|
||||
resip::Lock l(mNewQueuedGuard);
|
||||
mNewQueued.push_back(std::string((const char*)bufferptr, buffersize));
|
||||
|
||||
::SetEvent(mDataSignal);
|
||||
}
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void ReliableTunnel::sendData(InternetAddress& addr, const void* dataBuffer, unsigned int datasize)
|
||||
{
|
||||
switch (addr.type())
|
||||
{
|
||||
case AF_INET:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
|
||||
case AF_INET6:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTunnel::sessionEstablished(int conntype)
|
||||
{
|
||||
// Start worker thread
|
||||
if (conntype == EV_ICE)
|
||||
run();
|
||||
}
|
||||
|
||||
void ReliableTunnel::sessionTerminated()
|
||||
{
|
||||
// Stop worker thread
|
||||
::SetEvent(mExitSignal);
|
||||
shutdown();
|
||||
join();
|
||||
}
|
||||
|
||||
void ReliableTunnel::updateSdpOffer(resip::SdpContents::Session::Medium& sdp)
|
||||
{
|
||||
// Get new destination port
|
||||
mDestination.setPort((unsigned short)sdp.port());
|
||||
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec("rt", 104));
|
||||
}
|
||||
|
||||
void ReliableTunnel::setSocket(DatagramSocket& socket4, DatagramSocket& socket6)
|
||||
{
|
||||
mSocket4 = socket4;
|
||||
mSocket6 = socket6;
|
||||
}
|
||||
|
||||
DatagramSocket& ReliableTunnel::socket(int family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
return mSocket4;
|
||||
|
||||
case AF_INET6:
|
||||
return mSocket4;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media)
|
||||
{
|
||||
//check for default port number
|
||||
mDestination.setPort(media.port());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReliableTunnel::thread()
|
||||
{
|
||||
// Construct event array
|
||||
while (true)
|
||||
{
|
||||
HANDLE eventarray[2] = { mDataSignal, mExitSignal };
|
||||
|
||||
DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
|
||||
if (rescode == WAIT_OBJECT_0)
|
||||
{
|
||||
resip::Lock l(mNewQueuedGuard);
|
||||
for (unsigned i = 0; i<mNewQueued.size(); i++)
|
||||
mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size());
|
||||
mNewQueued.clear();
|
||||
|
||||
sendOutgoing();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTunnel::setBandwidth(unsigned int bytesPerSecond)
|
||||
{
|
||||
mBandwidth = bytesPerSecond;
|
||||
}
|
||||
|
||||
unsigned int ReliableTunnel::bandwidth()
|
||||
{
|
||||
return mBandwidth;
|
||||
}
|
||||
|
||||
void ReliableTunnel::processData(const void* dataptr, int datasize)
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
mStack.processIncoming(dataptr, datasize);
|
||||
}
|
||||
|
||||
bool ReliableTunnel::hasData()
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
return mIncomingData.size() || mStack.hasAppData();
|
||||
}
|
||||
|
||||
unsigned ReliableTunnel::getData(void* ptr, unsigned capacity)
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
char* dataOut = (char*)ptr;
|
||||
|
||||
while (capacity && hasData())
|
||||
{
|
||||
// Check if mIncomingData is empty
|
||||
if (!mIncomingData.size())
|
||||
{
|
||||
unsigned available = mStack.appData(NULL);
|
||||
if (!available)
|
||||
return 0;
|
||||
|
||||
mIncomingData.resize(available);
|
||||
mIncomingData.rewind();
|
||||
mStack.appData(mIncomingData.mutableData());
|
||||
}
|
||||
|
||||
if (mIncomingData.size())
|
||||
{
|
||||
unsigned toCopy = min(capacity, mIncomingData.size());
|
||||
mIncomingData.dequeueBuffer(dataOut, toCopy);
|
||||
dataOut += toCopy;
|
||||
capacity -= toCopy;
|
||||
}
|
||||
}
|
||||
|
||||
return dataOut - (char*)ptr;
|
||||
}
|
||||
|
||||
// Returns block size for encryption algorythm
|
||||
int ReliableTunnel::blockSize()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void ReliableTunnel::encrypt(void* dataPtr, int dataSize)
|
||||
{
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ENCRYPT);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void ReliableTunnel::decrypt(void* dataPtr, int dataSize)
|
||||
{
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
#ifdef USE_OPENSSL
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_DECRYPT);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calculates CRC
|
||||
unsigned ReliableTunnel::crc(const void* dataptr, int datasize)
|
||||
{
|
||||
unsigned long result;
|
||||
ICEImpl::CRC32 crc;
|
||||
crc.fullCrc((const unsigned char*)dataptr, datasize, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReliableTunnel::sendOutgoing()
|
||||
{
|
||||
// Check if stack has to send smth
|
||||
if (mStack.hasPacketToSend())
|
||||
{
|
||||
// Get data to send
|
||||
char buffer[2048];
|
||||
int length = sizeof(buffer);
|
||||
mStack.getPacketToSend(buffer, length);
|
||||
|
||||
// Send it over UDP
|
||||
sendData(this->mDestination, buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTunnel::setEncryptionKey(void* ptr, unsigned length)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
BF_set_key(&mCipher, length, (const unsigned char*)ptr);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
mEncryptor.SetKey((unsigned char*)ptr, length);
|
||||
mDecryptor.SetKey((unsigned char*)ptr, length);
|
||||
#endif
|
||||
|
||||
// Save key
|
||||
mEncryptionKey = std::string((const char*)ptr, length);
|
||||
}
|
||||
147
src/engine/endpoint/EP_ReliableTunnel.h
Normal file
147
src/engine/endpoint/EP_ReliableTunnel.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2010 Dmytro Bogovych <dmytro.bogovych@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __RELIABLE_TUNNEL_H
|
||||
#define __RELIABLE_TUNNEL_H
|
||||
|
||||
#include "DataProvider.h"
|
||||
#include "InternetAddress.h"
|
||||
#include "rutil/ThreadIf.hxx"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../ICE/ICEReliableTransport.h"
|
||||
#ifdef USE_CRYPTOPP
|
||||
# include "../Libs/CryptoPP/blowfish.h"
|
||||
#endif
|
||||
#ifdef USE_OPENSSL
|
||||
# include "../Libs/openssl/include/openssl/blowfish.h"
|
||||
#endif
|
||||
|
||||
class ReliableTunnel: public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption
|
||||
{
|
||||
public:
|
||||
ReliableTunnel(const char* streamname);
|
||||
virtual ~ReliableTunnel();
|
||||
|
||||
// Returns provider RTP name
|
||||
virtual std::string streamName();
|
||||
|
||||
// Returns provider RTP profile name
|
||||
virtual std::string streamProfile();
|
||||
|
||||
// Sets destination IP address
|
||||
virtual void setDestinationAddress(InternetAddress& addr);
|
||||
|
||||
// Processes incoming data
|
||||
virtual void processData(const void* dataBuffer, int dataSize);
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
|
||||
|
||||
// Updates SDP offer
|
||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp);
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
virtual void sessionTerminated();
|
||||
|
||||
// Called by user agent when session is started.
|
||||
virtual void sessionEstablished(int conntype);
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
virtual void setSocket(DatagramSocket& socket4, DatagramSocket& socket6);
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
virtual DatagramSocket& socket(int family);
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
|
||||
|
||||
virtual void thread();
|
||||
|
||||
// Enqueues outgoing packet to sending queue
|
||||
void queueData(const void* bufferPtr, int bufferSize);
|
||||
|
||||
void setBandwidth(unsigned int bytesPerSecond);
|
||||
unsigned int bandwidth();
|
||||
|
||||
// Checks if there is any received application data
|
||||
bool hasData();
|
||||
|
||||
// Reads received data. If ptr is NULL - the length of available data is returned.
|
||||
unsigned getData(void* ptr, unsigned capacity);
|
||||
|
||||
void setEncryptionKey(void* ptr, unsigned length);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
|
||||
// Transport stack
|
||||
ICEImpl::ReliableTransport mStack;
|
||||
|
||||
// Socket handles to operate
|
||||
DatagramSocket mSocket4;
|
||||
DatagramSocket mSocket6;
|
||||
|
||||
// Destination IP4/6 address
|
||||
InternetAddress mDestination;
|
||||
|
||||
// Win32 exit signal
|
||||
HANDLE mExitSignal;
|
||||
|
||||
// Win32 "new outgoing data" signal
|
||||
HANDLE mDataSignal;
|
||||
|
||||
// Mutex to protect queuing/sending outgoing data
|
||||
resip::Mutex mOutgoingMtx;
|
||||
|
||||
std::vector<std::string>
|
||||
mNewQueued;
|
||||
resip::Mutex mNewQueuedGuard;
|
||||
resip::Mutex mStackGuard;
|
||||
|
||||
unsigned int mBandwidth;
|
||||
std::string mEncryptionKey;
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
CryptoPP::BlowfishEncryption mEncryptor;
|
||||
CryptoPP::BlowfishDecryption mDecryptor;
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
BF_KEY mCipher;
|
||||
#endif
|
||||
|
||||
ICEImpl::ICEByteBuffer mIncomingData;
|
||||
|
||||
// Returns block size for encryption algorythm
|
||||
int blockSize();
|
||||
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void encrypt(void* dataPtr, int dataSize);
|
||||
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void decrypt(void* dataPtr, int dataSize);
|
||||
|
||||
// Calculates CRC
|
||||
unsigned crc(const void* dataptr, int datasize);
|
||||
|
||||
void sendOutgoing();
|
||||
};
|
||||
#endif
|
||||
1152
src/engine/endpoint/EP_Session.cpp
Normal file
1152
src/engine/endpoint/EP_Session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
410
src/engine/endpoint/EP_Session.h
Normal file
410
src/engine/endpoint/EP_Session.h
Normal file
@@ -0,0 +1,410 @@
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __SESSION_H
|
||||
#define __SESSION_H
|
||||
|
||||
#include "resip/stack/SdpContents.hxx"
|
||||
#include "resip/stack/SipMessage.hxx"
|
||||
#include "resip/stack/ShutdownMessage.hxx"
|
||||
#include "resip/stack/SipStack.hxx"
|
||||
#include "resip/dum/ClientAuthManager.hxx"
|
||||
#include "resip/dum/ClientInviteSession.hxx"
|
||||
#include "resip/dum/ClientRegistration.hxx"
|
||||
#include "resip/dum/DialogUsageManager.hxx"
|
||||
#include "resip/dum/DumShutdownHandler.hxx"
|
||||
#include "resip/dum/InviteSessionHandler.hxx"
|
||||
#include "resip/dum/MasterProfile.hxx"
|
||||
#include "resip/dum/RegistrationHandler.hxx"
|
||||
#include "resip/dum/ServerInviteSession.hxx"
|
||||
#include "resip/dum/ServerOutOfDialogReq.hxx"
|
||||
#include "resip/dum/OutOfDialogHandler.hxx"
|
||||
#include "resip/dum/AppDialog.hxx"
|
||||
#include "resip/dum/AppDialogSet.hxx"
|
||||
#include "resip/dum/AppDialogSetFactory.hxx"
|
||||
#include "rutil/Log.hxx"
|
||||
#include "rutil/Logger.hxx"
|
||||
#include "rutil/Random.hxx"
|
||||
#include "rutil/WinLeakCheck.hxx"
|
||||
#include "rutil/AtomicCounter.hxx"
|
||||
|
||||
#include "../ice/ICEBox.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "EP_Session.h"
|
||||
#include "EP_Account.h"
|
||||
#include "EP_DataProvider.h"
|
||||
#include "EP_AudioProvider.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "../helper/HL_SocketHeap.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class UserAgent;
|
||||
class ResipSession;
|
||||
|
||||
enum SessionInfo
|
||||
{
|
||||
SessionInfo_RemoteSipAddress, // remote sip address
|
||||
SessionInfo_ReceivedTraffic, // amount of received traffic in session in bytes
|
||||
SessionInfo_SentTraffic, // amount of sent traffic in session in bytes
|
||||
SessionInfo_PacketLoss, // lost packets counter; returns number of 1/1000 fractions (0.1%)
|
||||
SessionInfo_AudioPeer, // remote peer rtp address in text
|
||||
SessionInfo_AudioCodec, // selected audio codec as text
|
||||
SessionInfo_DtmfInterface, // Pointer to DtmfQueue class; returned as void*
|
||||
SessionInfo_IceState,
|
||||
SessionInfo_NetworkMos,
|
||||
SessionInfo_PvqaMos,
|
||||
SessionInfo_PvqaReport,
|
||||
SessionInfo_SentRtp,
|
||||
SessionInfo_SentRtcp,
|
||||
SessionInfo_ReceivedRtp,
|
||||
SessionInfo_ReceivedRtcp,
|
||||
SessionInfo_LostRtp,
|
||||
SessionInfo_Duration,
|
||||
SessionInfo_Jitter,
|
||||
SessionInfo_Rtt,
|
||||
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
||||
SessionInfo_RemotePeer,
|
||||
SessionInfo_SSRC
|
||||
};
|
||||
|
||||
|
||||
class Session :
|
||||
public SocketSink,
|
||||
public ice::StageHandler
|
||||
{
|
||||
public:
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
virtual void run(Session& s) = 0;
|
||||
};
|
||||
|
||||
// Describes ice stream/component
|
||||
struct IceInfo
|
||||
{
|
||||
IceInfo()
|
||||
:mStreamId(-1)
|
||||
{
|
||||
mPort4 = mPort6 = 0;
|
||||
mComponentId.mRtp = mComponentId.mRtcp = -1;
|
||||
}
|
||||
|
||||
RtpPair<int> mComponentId;
|
||||
int mStreamId;
|
||||
unsigned short mPort4;
|
||||
unsigned short mPort6;
|
||||
};
|
||||
|
||||
// Describes media stream (audio/video) in session
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
|
||||
void setProvider(PDataProvider provider);
|
||||
PDataProvider provider();
|
||||
|
||||
void setSocket4(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket4();
|
||||
|
||||
void setSocket6(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket6();
|
||||
|
||||
void setIceInfo(const IceInfo& info);
|
||||
IceInfo iceInfo() const;
|
||||
|
||||
// rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer
|
||||
bool rtcpAttr() const;
|
||||
void setRtcpAttr(bool value);
|
||||
|
||||
bool rtcpMuxAttr() const;
|
||||
void setRtcpMuxAttr(bool value);
|
||||
|
||||
protected:
|
||||
// Provider for corresponding stream
|
||||
PDataProvider mProvider;
|
||||
|
||||
// Socket for stream
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
|
||||
bool mRtcpAttr;
|
||||
bool mRtcpMuxAttr;
|
||||
IceInfo mIceInfo;
|
||||
};
|
||||
|
||||
Session(PAccount account);
|
||||
virtual ~Session();
|
||||
|
||||
// Starts call to specified peer
|
||||
void start(const std::string& peer);
|
||||
|
||||
// Stops call
|
||||
void stop();
|
||||
|
||||
// Accepts call
|
||||
void accept();
|
||||
|
||||
// Rejects call
|
||||
void reject(int code);
|
||||
|
||||
enum class InfoOptions
|
||||
{
|
||||
Standard = 0,
|
||||
Detailed = 1,
|
||||
};
|
||||
|
||||
void getSessionInfo(InfoOptions options, VariantMap& result);
|
||||
|
||||
// Returns integer identifier of the session; it is unique amongst all session in application
|
||||
int id() const;
|
||||
|
||||
// Returns owning account
|
||||
PAccount account();
|
||||
|
||||
typedef std::map<std::string, std::string> UserHeaders;
|
||||
void setUserHeaders(const UserHeaders& headers);
|
||||
|
||||
// Called when new media data are available for this session
|
||||
void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize);
|
||||
|
||||
// Called when new candidate is gathered
|
||||
void onCandidateGathered(ice::Stack* stack, void* tag, const char* address);
|
||||
|
||||
// Called when connectivity check is finished
|
||||
void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription);
|
||||
|
||||
// Called when ICE candidates are gathered - with success or timeout.
|
||||
void onGathered(ice::Stack* stack, void* tag);
|
||||
|
||||
// Called when ICE connectivity check is good at least for one of required streams
|
||||
void onSuccess(ice::Stack* stack, void* tag);
|
||||
|
||||
// Called when ICE connectivity check is failed for all of required streams
|
||||
void onFailed(ice::Stack* stack, void* tag);
|
||||
|
||||
// Called when ICE stack detects network change during the call
|
||||
void onNetworkChange(ice::Stack* stack, void* tag);
|
||||
|
||||
// Fills SDP according to ICE and provider's data
|
||||
void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection);
|
||||
|
||||
// Searches provider by its local port number
|
||||
PDataProvider findProviderByPort(int family, unsigned short port);
|
||||
|
||||
// Add provider to internal list
|
||||
void addProvider(PDataProvider provider);
|
||||
PDataProvider providerAt(int index);
|
||||
int getProviderCount();
|
||||
|
||||
void setUserAgent(UserAgent* agent);
|
||||
UserAgent* userAgent();
|
||||
|
||||
// Pauses and resumes all providers; updates states
|
||||
void pause();
|
||||
void resume();
|
||||
void refreshMediaPath();
|
||||
|
||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||
|
||||
// Session ID
|
||||
int mSessionId;
|
||||
|
||||
// Media streams collection
|
||||
std::vector<Stream> mStreamList;
|
||||
|
||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||
resip::SharedPtr<ice::Stack> mIceStack;
|
||||
|
||||
// Pointer to owner user agent instance
|
||||
UserAgent* mUserAgent;
|
||||
|
||||
// Remote peer SIP address
|
||||
resip::NameAddr mRemotePeer;
|
||||
|
||||
// Mutex to protect this instance
|
||||
Mutex mGuard;
|
||||
|
||||
// SDP's origin version for sending
|
||||
int mOriginVersion;
|
||||
UInt64 mRemoteOriginVersion;
|
||||
|
||||
// SDP's session version
|
||||
int mSessionVersion;
|
||||
|
||||
// Marks if this session does not need OnNewSession event
|
||||
bool mAcceptedByEngine;
|
||||
bool mAcceptedByUser;
|
||||
|
||||
// Invite session handle
|
||||
resip::InviteSessionHandle mInviteHandle;
|
||||
|
||||
// Dialog set object pointer
|
||||
ResipSession* mResipSession;
|
||||
|
||||
// Reference counter
|
||||
int mRefCount;
|
||||
|
||||
enum
|
||||
{
|
||||
Initiator = 1,
|
||||
Acceptor = 2
|
||||
};
|
||||
|
||||
// Specifies session role - caller (Initiator) or callee (Acceptor)
|
||||
volatile int mRole;
|
||||
|
||||
// Marks if candidates are gather already
|
||||
volatile bool mGatheredCandidates;
|
||||
|
||||
// Marks if OnTerminated event was called already on session
|
||||
volatile bool mTerminated;
|
||||
|
||||
// User friend remote peer's sip address
|
||||
std::string mRemoteAddress;
|
||||
|
||||
// Application specific data
|
||||
void* mTag;
|
||||
|
||||
// Used to count number of transistions to Connected state and avoid multiple onEstablished events.
|
||||
int mOfferAnswerCounter;
|
||||
|
||||
// List of turn prefixes related to sessioj
|
||||
std::vector<int> mTurnPrefixList;
|
||||
|
||||
// True if user agent has to send offer
|
||||
bool mHasToSendOffer;
|
||||
|
||||
// True if user agent has to enqueue offer after ice gather finished
|
||||
bool mSendOfferUpdateAfterIceGather;
|
||||
|
||||
// Related sip account
|
||||
PAccount mAccount;
|
||||
|
||||
// User headers for INVITE transaction
|
||||
UserHeaders mUserHeaders;
|
||||
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(const std::string& address);
|
||||
|
||||
void* tag();
|
||||
void setTag(void* tag);
|
||||
int sessionId();
|
||||
int increaseSdpVersion();
|
||||
int addRef();
|
||||
int release();
|
||||
|
||||
// Deletes providers and media sockets
|
||||
void clearProvidersAndSockets();
|
||||
|
||||
// Deletes providers
|
||||
void clearProviders();
|
||||
|
||||
// Helper method to find audio provider for active sip stream
|
||||
AudioProvider* findProviderForActiveAudio();
|
||||
|
||||
void processCommandList();
|
||||
void addCommand(Command* cmd);
|
||||
void enqueueOffer();
|
||||
void processQueuedOffer();
|
||||
static int generateId();
|
||||
static resip::AtomicCounter IdGenerator;
|
||||
static resip::AtomicCounter InstanceCounter;
|
||||
};
|
||||
|
||||
typedef SharedPtr<Session> PSession;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Classes that provide the mapping between Application Data and DUM
|
||||
// dialogs/dialogsets
|
||||
//
|
||||
// The DUM layer creates an AppDialog/AppDialogSet object for inbound/outbound
|
||||
// SIP Request that results in Dialog creation.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
class ResipSessionAppDialog : public resip::AppDialog
|
||||
{
|
||||
public:
|
||||
ResipSessionAppDialog(resip::HandleManager& ham);
|
||||
virtual ~ResipSessionAppDialog();
|
||||
};
|
||||
|
||||
class ResipSession: public resip::AppDialogSet
|
||||
{
|
||||
friend class UserAgent;
|
||||
friend class Account;
|
||||
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Type_None,
|
||||
Type_Registration,
|
||||
Type_Subscription,
|
||||
Type_Call,
|
||||
Type_Auto
|
||||
};
|
||||
static resip::AtomicCounter InstanceCounter;
|
||||
|
||||
|
||||
ResipSession(resip::DialogUsageManager& dum);
|
||||
virtual ~ResipSession();
|
||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||
|
||||
void setType(Type type);
|
||||
Type type();
|
||||
|
||||
Session* session();
|
||||
void setSession(Session* session);
|
||||
|
||||
UserAgent* ua();
|
||||
void setUa(UserAgent* ua);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
int sessionId();
|
||||
|
||||
// Used for subscriptions/messages
|
||||
void* tag() const;
|
||||
void setTag(void* tag);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(std::string address);
|
||||
|
||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||
|
||||
void setUASProfile(SharedPtr<resip::UserProfile> profile);
|
||||
|
||||
protected:
|
||||
bool mTerminated;
|
||||
UserAgent* mUserAgent;
|
||||
Type mType;
|
||||
Session* mSession;
|
||||
int mSessionId;
|
||||
std::string mRemoteAddress;
|
||||
void* mTag;
|
||||
bool mOnWatchingStartSent;
|
||||
SharedPtr<resip::UserProfile> mUASProfile;
|
||||
};
|
||||
|
||||
|
||||
class ResipSessionFactory : public resip::AppDialogSetFactory
|
||||
{
|
||||
public:
|
||||
ResipSessionFactory(UserAgent* agent);
|
||||
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
|
||||
protected:
|
||||
UserAgent* mAgent;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user