Commit 82a0e238 authored by Adrien Béraud's avatar Adrien Béraud Committed by Tristan Matthews

sip: transport refactoring

SIPTransport now represents a SIP transport.
The old SIPTransport class becomes SIPTransportBroker.
Makes every sip call have its own transport, to allow IP2IP calls
using TLS.

Refs #53057
Change-Id: I6ae8e1a4c681c6f4f5887772f5b852bd440df13f
parent ff4ee38a
......@@ -204,5 +204,4 @@ void AccountFactory::initIP2IPAccount()
// cache this often used account using a weak_ptr
ip2ip_account_ = createAccount(SIPAccount::ACCOUNT_TYPE,
SIPAccount::IP2IP_PROFILE);
SIPVoIPLink::loadIP2IPSettings();
}
......@@ -192,6 +192,14 @@ SIPAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
toUri = getToUri(to);
family = ipv6 ? pj_AF_INET6() : pj_AF_INET();
std::shared_ptr<SipTransport> t =
#if HAVE_TLS
isTlsEnabled() ? link_->sipTransport->getTlsTransport(tlsListener_, toUri) :
#endif
transport_;
setTransport(t);
call->setTransport(t);
DEBUG("New %s IP to IP call to %s", ipv6?"IPv6":"IPv4", to.c_str());
}
else {
......@@ -204,6 +212,7 @@ SIPAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
else
toUri = getToUri(to);
call->setTransport(transport_);
// FIXME : for now, use the same address family as the SIP tranport
family = pjsip_transport_type_get_af(getTransportType());
......@@ -328,7 +337,7 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
return false;
}
const pjsip_tpselector tp_sel = getTransportSelector();
const pjsip_tpselector tp_sel = SipTransportBroker::getTransportSelector(call->getTransport()->get());
if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
ERROR("Unable to associate transport for invite session dialog");
return false;
......@@ -687,9 +696,21 @@ void SIPAccount::doRegister()
transportType_ = IPv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS;
initTlsConfiguration();
if (!tlsListener_) {
tlsListener_ = link_->sipTransport->getTlsListener(
SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
getTlsSetting());
if (!tlsListener_) {
setRegistrationState(RegistrationState::ERROR_GENERIC);
ERROR("Error creating TLS listener.");
return;
}
}
} else
#endif
{
tlsListener_.reset();
transportType_ = IPv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP;
}
......@@ -703,14 +724,36 @@ void SIPAccount::doRegister()
// In our definition of the ip2ip profile (aka Direct IP Calls),
// no registration should be performed
if (isIP2IP())
if (isIP2IP()) {
// If we use Tls for IP2IP, transports will be created on connection.
if (!tlsEnable_)
setTransport(link_->sipTransport->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
return;
}
try {
WARN("Creating transport");
transport_.reset();
#if HAVE_TLS
if (isTlsEnabled()) {
setTransport(link_->sipTransport->getTlsTransport(tlsListener_, getServerUri()));
} else
#endif
{
setTransport(link_->sipTransport->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
}
if (!transport_)
throw VoipLinkException("Can't create transport");
sendRegister();
} catch (const VoipLinkException &e) {
ERROR("%s", e.what());
setRegistrationState(RegistrationState::ERROR_GENERIC);
return;
}
#ifdef SFL_PRESENCE
......@@ -723,6 +766,7 @@ void SIPAccount::doRegister()
void SIPAccount::doUnregister(std::function<void(bool)> released_cb)
{
tlsListener_.reset();
if (isIP2IP()) {
if (released_cb)
released_cb(false);
......@@ -730,18 +774,20 @@ void SIPAccount::doUnregister(std::function<void(bool)> released_cb)
}
try {
sendUnregister(released_cb);
sendUnregister();
} catch (const VoipLinkException &e) {
ERROR("doUnregister %s", e.what());
setTransport();
if (released_cb)
released_cb(false);
}
// remove the transport from the account
if (transport_)
setTransport();
if (released_cb)
released_cb(true);
}
void SIPAccount::startKeepAliveTimer()
{
if (isTlsEnabled())
return;
......@@ -794,13 +840,6 @@ SIPAccount::sendRegister()
return;
}
try {
link_->sipTransport->createSipTransport(*this);
} catch (const std::runtime_error &e) {
ERROR("%s", e.what());
throw VoipLinkException("Could not create or acquire SIP transport");
}
setRegister(true);
setRegistrationState(RegistrationState::TRYING);
......@@ -826,10 +865,10 @@ SIPAccount::sendRegister()
pjsip_host_port *via = getViaAddr();
DEBUG("Setting VIA sent-by to %.*s:%d", via->host.slen, via->host.ptr, via->port);
if (pjsip_regc_set_via_sent_by(regc, via, transport_) != PJ_SUCCESS)
if (pjsip_regc_set_via_sent_by(regc, via, transport_->get()) != PJ_SUCCESS)
throw VoipLinkException("Unable to set the \"sent-by\" field");
} else if (isStunEnabled()) {
if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_) != PJ_SUCCESS)
if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_->get()) != PJ_SUCCESS)
throw VoipLinkException("Unable to set the \"sent-by\" field");
}
}
......@@ -861,7 +900,7 @@ SIPAccount::sendRegister()
if (pjsip_regc_register(regc, PJ_TRUE, &tdata) != PJ_SUCCESS)
throw VoipLinkException("Unable to initialize transaction data for account registration");
const pjsip_tpselector tp_sel = SipTransport::getTransportSelector(transport_);
const pjsip_tpselector tp_sel = getTransportSelector();
if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
throw VoipLinkException("Unable to set transport");
......@@ -872,7 +911,6 @@ SIPAccount::sendRegister()
}
setRegistrationInfo(regc);
link_->sipTransport->cleanupTransports();
}
void
......@@ -972,13 +1010,11 @@ SIPAccount::onRegister(pjsip_regc_cbparam *param)
}
void
SIPAccount::sendUnregister(std::function<void(bool)> released_cb)
SIPAccount::sendUnregister()
{
// This may occurs if account failed to register and is in state INVALID
if (!isRegistered()) {
setRegistrationState(RegistrationState::UNREGISTERED);
if (released_cb)
released_cb(true);
return;
}
......@@ -1000,13 +1036,6 @@ SIPAccount::sendUnregister(std::function<void(bool)> released_cb)
}
setRegister(false);
// remove the transport from the account
auto transport = getTransport();
setTransport();
link_->sipTransport->cleanupTransports();
if (released_cb)
link_->sipTransport->waitForReleased(transport, released_cb);
}
#if HAVE_TLS
......@@ -1243,7 +1272,7 @@ std::string SIPAccount::getServerUri() const
pj_str_t
SIPAccount::getContactHeader()
{
if (transport_ == nullptr)
if (!transport_)
ERROR("Transport not created yet");
if (contact_.slen and contactOverwritten_)
......@@ -1259,14 +1288,22 @@ SIPAccount::getContactHeader()
std::string address;
pj_uint16_t port;
link_->sipTransport->findLocalAddressFromTransport(transport_, transportType, hostname_, address, port);
link_->sipTransport->findLocalAddressFromTransport(
transport_ ? transport_->get() : nullptr,
transportType,
hostname_,
address, port);
if (not publishedSameasLocal_) {
address = publishedIpAddress_;
port = publishedPort_;
DEBUG("Using published address %s and port %d", address.c_str(), port);
} else if (stunEnabled_) {
link_->sipTransport->findLocalAddressFromSTUN(transport_, &stunServerName_, stunPort_, address, port);
link_->sipTransport->findLocalAddressFromSTUN(
transport_ ? transport_->get() : nullptr,
&stunServerName_,
stunPort_,
address, port);
setPublishedAddress(address);
publishedPort_ = port;
usePublishedAddressPortInVIA();
......@@ -1317,7 +1354,11 @@ SIPAccount::getHostPortFromSTUN(pj_pool_t *pool)
{
std::string addr;
pj_uint16_t port;
link_->sipTransport->findLocalAddressFromSTUN(transport_, &stunServerName_, stunPort_, addr, port);
link_->sipTransport->findLocalAddressFromSTUN(
transport_ ? transport_->get() : nullptr,
&stunServerName_,
stunPort_,
addr, port);
pjsip_host_port result;
pj_strdup2(pool, &result.host, addr.c_str());
result.host.slen = addr.length();
......@@ -1380,19 +1421,13 @@ computeMd5HashFromCredential(const std::string& username,
}
void
SIPAccount::setTransport(pjsip_transport* transport, pjsip_tpfactory* lis)
SIPAccount::setTransport(const std::shared_ptr<SipTransport>& t)
{
// release old transport
if (transport_ && transport_ != transport) {
if (regc_)
pjsip_regc_release_transport(regc_);
pjsip_transport_dec_ref(transport_);
}
if (tlsListener_ && tlsListener_ != lis)
tlsListener_->destroy(tlsListener_);
// set new transport
transport_ = transport;
tlsListener_ = lis;
if (transport_ == t)
return;
if (transport_ && regc_)
pjsip_regc_release_transport(regc_);
SIPAccountBase::setTransport(t);
}
void SIPAccount::setCredentials(const std::vector<std::map<std::string, std::string> >& creds)
......@@ -1794,7 +1829,10 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam *param, pj_pool_t *pool)
pj_assert(contactRewriteMethod_ == 1 or contactRewriteMethod_ == 2);
std::shared_ptr<SipTransport> tmp_tp {nullptr};
if (contactRewriteMethod_ == 1) {
/* Save transport in case we're gonna reuse it */
tmp_tp = transport_;
/* Unregister current contact */
sendUnregister();
destroyRegistrationInfo();
......@@ -1841,6 +1879,7 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam *param, pj_pool_t *pool)
/* Unregister old contact */
try {
tmp_tp = transport_;
sendUnregister();
} catch (const VoipLinkException &e) {
ERROR("%s", e.what());
......
......@@ -174,7 +174,7 @@ class SIPAccount : public SIPAccountBase {
* Build and send SIP unregistration request
* @param destroy_transport If true, attempt to destroy the transport.
*/
void sendUnregister(std::function<void(bool)> cb = std::function<void(bool)>());
void sendUnregister();
const pjsip_cred_info* getCredInfo() const {
return cred_.data();
......@@ -405,7 +405,7 @@ class SIPAccount : public SIPAccountBase {
return keepAliveEnabled_;
}
void setTransport(pjsip_transport* transport = nullptr, pjsip_tpfactory* lis = nullptr);
virtual void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
/* Returns true if the username and/or hostname match this account */
MatchRank matches(const std::string &username, const std::string &hostname, pjsip_endpoint *endpt, pj_pool_t *pool) const;
......
......@@ -235,19 +235,37 @@ SIPAccountBase::getAccountDetails() const
return a;
}
void
SIPAccountBase::onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info *info)
{
DEBUG("Transport state changed to %s for account %s !", SipTransport::stateToStr(state), accountID_.c_str());
if (!SipTransport::isAlive(transport_, state)) {
if (info) {
char err_msg[128];
err_msg[0] = '\0';
pj_str_t descr = pj_strerror(info->status, err_msg, sizeof(err_msg));
ERROR("Transport disconnected: %.*s", descr.slen, descr.ptr);
}
setRegistrationState(RegistrationState::ERROR_GENERIC);
setTransport();
}
}
void
SIPAccountBase::setTransport(pjsip_transport* transport, pjsip_tpfactory* lis)
SIPAccountBase::setTransport(const std::shared_ptr<SipTransport>& t)
{
// release old transport
if (transport_ && transport_ != transport) {
pjsip_transport_dec_ref(transport_);
using namespace std::placeholders;
if (t == transport_)
return;
if (transport_) {
DEBUG("Removing transport from account");
transport_->removeStateListener(reinterpret_cast<uintptr_t>(this));
}
if (tlsListener_ && tlsListener_ != lis)
tlsListener_->destroy(tlsListener_);
// set new transport
transport_ = transport;
tlsListener_ = lis;
transport_ = t;
if (transport_)
transport_->addStateListener(reinterpret_cast<uintptr_t>(this), std::bind(&SIPAccountBase::onTransportStateChanged, this, _1, _2));
}
// returns even number in range [lower, upper]
......
......@@ -126,8 +126,9 @@ public:
*/
SIPAccountBase(const std::string& accountID);
virtual ~SIPAccountBase() = default;
virtual ~SIPAccountBase() {
setTransport();
}
/**
* Create incoming SIPCall.
......@@ -265,12 +266,12 @@ public:
#endif
static void releasePort(uint16_t port);
inline pjsip_transport* getTransport() {
virtual void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
inline const std::shared_ptr<SipTransport>& getTransport() {
return transport_;
}
virtual void setTransport(pjsip_transport* transport = nullptr, pjsip_tpfactory* lis = nullptr);
inline pjsip_transport_type_e getTransportType() const {
return transportType_;
}
......@@ -279,10 +280,11 @@ public:
* Shortcut for SipTransport::getTransportSelector(account.getTransport()).
*/
inline pjsip_tpselector getTransportSelector() {
return SipTransport::getTransportSelector(transport_);
if (!transport_)
return SipTransportBroker::getTransportSelector(nullptr);
return SipTransportBroker::getTransportSelector(transport_->get());
}
protected:
virtual void serialize(YAML::Emitter &out);
virtual void unserialize(const YAML::Node &node);
......@@ -292,14 +294,19 @@ protected:
virtual std::map<std::string, std::string> getAccountDetails() const;
/**
* Voice over IP Link contains a listener thread and calls
* Callback called by the transport layer when the registration
* transport state changes.
*/
std::shared_ptr<SIPVoIPLink> link_;
virtual void onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info *info);
/**
* Pointer to the transport used by this acccount
* Voice over IP Link contains a listener thread and calls
*/
pjsip_transport* transport_ {nullptr};
std::shared_ptr<SIPVoIPLink> link_;
std::shared_ptr<SipTransport> transport_ {};
std::shared_ptr<TlsListener> tlsListener_ {};
/**
* Transport type used for this sip account. Currently supported types:
......@@ -337,11 +344,6 @@ protected:
*/
pj_uint16_t publishedPort_ {DEFAULT_SIP_PORT};
/**
* If a TLS tranport, pointer to the tls listener.
*/
pjsip_tpfactory* tlsListener_ {nullptr};
/**
* The global TLS listener port which can be configured through the IP2IP_PROFILE
*/
......
......@@ -57,6 +57,7 @@ struct pjsip_inv_session;
class Sdp;
class SIPAccountBase;
class SipTransport;
/**
* @file sipcall.h
......@@ -122,6 +123,14 @@ class SIPCall : public Call
void setContactHeader(pj_str_t *contact);
void setTransport(const std::shared_ptr<SipTransport>& t) {
transport_ = t;
}
inline const std::shared_ptr<SipTransport>& getTransport() {
return transport_;
}
void sendSIPInfo(const char *const body, const char *const subtype);
void answer();
......@@ -199,6 +208,13 @@ class SIPCall : public Call
sfl_video::VideoRtpSession videortp_;
#endif
/**
* Hold the transport used for SIP communication.
* Will be different from the account registration transport for
* non-IP2IP calls.
*/
std::shared_ptr<SipTransport> transport_ {};
/**
* The pool to allocate memory, released once call hang up
*/
......
This diff is collapsed.
This diff is collapsed.
......@@ -289,7 +289,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
auto call = sipaccount->newIncomingCall(Manager::instance().getNewCallID());
// FIXME : for now, use the same address family as the SIP tranport
// FIXME : for now, use the same address family as the SIP transport
auto family = pjsip_transport_type_get_af(sipaccount->getTransportType());
IpAddr addrToUse = ip_utils::getInterfaceAddr(sipaccount->getLocalInterface(), family);
......@@ -297,8 +297,6 @@ transaction_request_cb(pjsip_rx_data *rdata)
IpAddr addrSdp = sipaccount->isStunEnabled() or (not sipaccount->getPublishedSameasLocal())
? sipaccount->getPublishedIpAddress() : addrToUse;
pjsip_tpselector tp_sel = sipaccount->getTransportSelector();
char tmp[PJSIP_MAX_URL_SIZE];
size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
std::string peerNumber(tmp, std::min(length, sizeof tmp));
......@@ -307,8 +305,19 @@ transaction_request_cb(pjsip_rx_data *rdata)
if (not remote_user.empty() and not remote_hostname.empty())
peerNumber = remote_user + "@" + remote_hostname;
//DEBUG("transaction_request_cb viaHostname %s toUsername %s addrToUse %s addrSdp %s peerNumber: %s" ,
//viaHostname.c_str(), toUsername.c_str(), addrToUse.toString().c_str(), addrSdp.toString().c_str(), peerNumber.c_str());
// DEBUG("transaction_request_cb viaHostname %s toUsername %s addrToUse %s addrSdp %s peerNumber: %s" ,
// viaHostname.c_str(), toUsername.c_str(), addrToUse.toString().c_str(), addrSdp.toString().c_str(), peerNumber.c_str());
auto transport = getSIPVoIPLink()->sipTransport->findTransport(rdata->tp_info.transport);
if (!transport) {
transport = sipaccount->getTransport();
if (!transport) {
ERROR("No suitable transport to answer this call.");
return PJ_FALSE;
} else {
WARN("Using transport from account.");
}
}
call->setConnectionState(Call::PROGRESSING);
call->setPeerNumber(peerNumber);
......@@ -317,6 +326,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
call->setCallMediaLocal(addrToUse);
call->getLocalSDP().setPublishedIP(addrSdp);
call->getAudioRtp().initConfig();
call->setTransport(transport);
try {
call->getAudioRtp().initSession();
......@@ -381,6 +391,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
return PJ_FALSE;
}
pjsip_tpselector tp_sel = SipTransportBroker::getTransportSelector(transport->get());
if (!dialog or pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
ERROR("Could not set transport for dialog");
return PJ_FALSE;
......@@ -492,11 +503,6 @@ pj_pool_t* SIPVoIPLink::getPool() const
}
SIPVoIPLink::SIPVoIPLink()
: sipTransport()
#ifdef SFL_VIDEO
, keyframeRequestsMutex_()
, keyframeRequests_()
#endif
{
DEBUG("creating SIPVoIPLink instance");
......@@ -522,7 +528,7 @@ SIPVoIPLink::SIPVoIPLink()
TRY(pjsip_endpt_create(&cp_->factory, pj_gethostname()->ptr, &endpt_));
sipTransport.reset(new SipTransport(endpt_, *cp_, *pool_));
sipTransport.reset(new SipTransportBroker(endpt_, *cp_, *pool_));
if (!ip_utils::getLocalAddr())
throw VoipLinkException("UserAgent: Unable to determine network capabilities");
......@@ -835,6 +841,7 @@ invite_session_state_changed_cb(pjsip_inv_session *inv, pjsip_event *ev)
case PJSIP_SC_REQUEST_PENDING:
case PJSIP_SC_ADDRESS_INCOMPLETE:
default:
WARN("PJSIP_INV_STATE_DISCONNECTED: %d %d", inv->cause, ev->type);
call->onServerFailure();
break;
}
......@@ -872,7 +879,7 @@ sdp_create_offer_cb(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
const auto& account = call->getSIPAccount();
// FIXME : for now, use the same address family as the SIP tranport
// FIXME : for now, use the same address family as the SIP transport
auto family = pjsip_transport_type_get_af(account.getTransportType());
IpAddr address = account.getPublishedSameasLocal()
? IpAddr(ip_utils::getInterfaceAddr(account.getLocalInterface(), family))
......@@ -1274,19 +1281,3 @@ int SIPVoIPLink::getModId()
void SIPVoIPLink::createSDPOffer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
{ sdp_create_offer_cb(inv, p_offer); }
void
SIPVoIPLink::loadIP2IPSettings()
{
try {
auto account = Manager::instance().getIP2IPAccount();
if (!account) {
ERROR("No existing IP2IP account");
return;
}
account->doRegister();
getSIPVoIPLink()->sipTransport->createSipTransport((SIPAccount&)*account);
} catch (const std::runtime_error &e) {
ERROR("%s", e.what());
}
}
......@@ -117,8 +117,6 @@ class SIPVoIPLink {
*/
void createDefaultSipUdpTransport();
static void loadIP2IPSettings();
public:
static void createSDPOffer(pjsip_inv_session *inv,
pjmedia_sdp_session **p_offer);
......@@ -126,7 +124,7 @@ class SIPVoIPLink {
/**
* Instance that maintain and manage transport (UDP, TLS)
*/
std::unique_ptr<SipTransport> sipTransport;
std::unique_ptr<SipTransportBroker> sipTransport {};
#ifdef SFL_VIDEO
static void enqueueKeyframeRequest(const std::string &callID);
......@@ -157,8 +155,8 @@ class SIPVoIPLink {
#ifdef SFL_VIDEO
void dequeKeyframeRequests();
void requestKeyframe(const std::string &callID);
std::mutex keyframeRequestsMutex_;
std::queue<std::string> keyframeRequests_;
std::mutex keyframeRequestsMutex_ {};
std::queue<std::string> keyframeRequests_ {};
#endif
static pj_caching_pool* cp_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment