From 82a0e238f3ed4b922d05f51a268458218c10c035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Mon, 6 Oct 2014 14:16:57 -0400 Subject: [PATCH] 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 --- daemon/src/account_factory.cpp | 1 - daemon/src/sip/sipaccount.cpp | 127 ++++++--- daemon/src/sip/sipaccount.h | 4 +- daemon/src/sip/sipaccountbase.cpp | 36 ++- daemon/src/sip/sipaccountbase.h | 34 +-- daemon/src/sip/sipcall.h | 16 ++ daemon/src/sip/siptransport.cpp | 440 ++++++++++++++---------------- daemon/src/sip/siptransport.h | 289 ++++++++++++-------- daemon/src/sip/sipvoiplink.cpp | 47 ++-- daemon/src/sip/sipvoiplink.h | 8 +- 10 files changed, 560 insertions(+), 442 deletions(-) diff --git a/daemon/src/account_factory.cpp b/daemon/src/account_factory.cpp index f84319b45e..73bf24cba6 100644 --- a/daemon/src/account_factory.cpp +++ b/daemon/src/account_factory.cpp @@ -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(); } diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp index be4823bfb6..14a097a6b6 100644 --- a/daemon/src/sip/sipaccount.cpp +++ b/daemon/src/sip/sipaccount.cpp @@ -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()); diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h index f9d1b0fcb5..3120820eac 100644 --- a/daemon/src/sip/sipaccount.h +++ b/daemon/src/sip/sipaccount.h @@ -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; diff --git a/daemon/src/sip/sipaccountbase.cpp b/daemon/src/sip/sipaccountbase.cpp index 0d54a8db81..f030cd1a64 100644 --- a/daemon/src/sip/sipaccountbase.cpp +++ b/daemon/src/sip/sipaccountbase.cpp @@ -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] diff --git a/daemon/src/sip/sipaccountbase.h b/daemon/src/sip/sipaccountbase.h index c9179b1db3..cd9bc470ee 100644 --- a/daemon/src/sip/sipaccountbase.h +++ b/daemon/src/sip/sipaccountbase.h @@ -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 */ diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h index 6191eba23d..5244e71a24 100644 --- a/daemon/src/sip/sipcall.h +++ b/daemon/src/sip/sipcall.h @@ -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 */ diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp index 7d39ce8ff3..3fbbe5b928 100644 --- a/daemon/src/sip/siptransport.cpp +++ b/daemon/src/sip/siptransport.cpp @@ -32,16 +32,15 @@ #include "siptransport.h" #include "sipaccount.h" -#include "sipvoiplink.h" #include "sip_utils.h" #include "manager.h" #include "client/configurationmanager.h" #include "map_utils.h" #include "ip_utils.h" +#include "array_size.h" #include <pjsip.h> -#include <pjsip_ua.h> #include <pjsip/sip_types.h> #if HAVE_TLS #include <pjsip/sip_transport_tls.h> @@ -53,327 +52,311 @@ #include <stdexcept> #include <sstream> +#include <algorithm> #define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); return (VAL); } -SipTransport::SipTransport(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool) : -transportMap_(), transportMapMutex_(), transportDestroyedCv_(), cp_(cp), pool_(pool), endpt_(endpt) +// FIXME: remove this when pjsip_tp_state_callback gives us enough info +static SipTransportBroker* instance = nullptr; + +constexpr const char* TRANSPORT_STATE_STR[] = { + "CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE" +}; +constexpr const size_t TRANSPORT_STATE_SZ = ARRAYSIZE(TRANSPORT_STATE_STR); + +std::string +SipTransportDescr::toString() const +{ + std::stringstream ss; + ss << "{" << pjsip_transport_get_type_desc(type) << " on " << interface << ":" << listenerPort << "}"; + return ss.str(); +} + +SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l) : +transport(t), tlsListener(l) +{ + pjsip_transport_add_ref(transport); +} + +SipTransport::~SipTransport() { - auto status = pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), SipTransport::tp_state_callback); + if (transport) { + pjsip_transport_shutdown(transport); + pjsip_transport_dec_ref(transport); // ?? + DEBUG("Destroying transport (refcount: %u)", pj_atomic_get(transport->ref_cnt)); + transport = nullptr; + } +} + +bool +SipTransport::isAlive(const std::shared_ptr<SipTransport>& t, pjsip_transport_state state) +{ + return state != PJSIP_TP_STATE_DISCONNECTED +#if PJ_VERSION_NUM > (2 << 24 | 1 << 16) + && state != PJSIP_TP_STATE_SHUTDOWN + && state != PJSIP_TP_STATE_DESTROY +#else + && t && t->get() + && !t->get()->is_shutdown + && !t->get()->is_destroying +#endif + ; +} + +const char* +SipTransport::stateToStr(pjsip_transport_state state) +{ + return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ-1)]; +} + +SipTransportBroker::SipTransportBroker(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool) : +cp_(cp), pool_(pool), endpt_(endpt) +{ + instance = this; + auto status = pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), SipTransportBroker::tp_state_callback); if (status != PJ_SUCCESS) { ERROR("Can't set transport callback"); sip_utils::sip_strerror(status); } } -SipTransport::~SipTransport() +SipTransportBroker::~SipTransportBroker() { + instance = nullptr; pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), nullptr); } -static std::string -transportMapKey(const std::string &interface, int port, pjsip_transport_type_e type) -{ - std::ostringstream os; - auto family = pjsip_transport_type_get_af(type); - char af_ver_num = (family == pj_AF_INET6()) ? '6' : '4'; - if (type == PJSIP_TRANSPORT_START_OTHER) // STUN - type = (family == pj_AF_INET6()) ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP; - os << interface << ':' << port << ':' << pjsip_transport_get_type_name(type) << af_ver_num; - return os.str(); -} - /** Static tranport state change callback */ void -SipTransport::tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info* info) +SipTransportBroker::tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info* info) { - SipTransport& this_ = *getSIPVoIPLink()->sipTransport; - this_.transportStateChanged(tp, state, info); + if (!instance) { + ERROR("Can't bubble event: SipTransportBroker instance is null !"); + return; + } + instance->transportStateChanged(tp, state, info); } void -SipTransport::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info) +SipTransportBroker::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info) { - std::lock_guard<std::mutex> lock(transportMapMutex_); - auto transport_key = map_utils::findByValue(transportMap_, tp); - if (transport_key == transportMap_.cend()) - return; + WARN("Transport %s -> %s", tp->info, SipTransport::stateToStr(state)); + { + std::shared_ptr<SipTransport> transport {}; + { + // The mutex is unlocked so the callback can lock it. + std::lock_guard<std::mutex> lock(transportMapMutex_); + auto t = transports_.find(tp); + if (t != transports_.end()) { + transport = t->second.lock(); + } + } + // Propagate the event to the appropriate transport. + if (transport) + transport->stateCallback(state, info); + } + #if PJ_VERSION_NUM > (2 << 24 | 1 << 16) - if (state == PJSIP_TP_STATE_SHUTDOWN || state == PJSIP_TP_STATE_DESTROY) { + if (state == PJSIP_TP_STATE_DESTROY) { #else - if (tp->is_shutdown || tp->is_destroying) { + if (tp->is_destroying) { #endif - char err_msg[128]; - err_msg[0] = '\0'; + std::lock_guard<std::mutex> lock(transportMapMutex_); - pj_str_t description; - if (info) { - description = pjsip_strerror(info->status, err_msg, sizeof(err_msg)); + // Transport map cleanup + auto t = transports_.find(tp); + if (t != transports_.end() && t->second.expired()) + transports_.erase(t); + + // If UDP + if (std::strlen(tp->type_name) >= 3 && std::strncmp(tp->type_name, "UDP", 3ul) == 0) { + auto transport_key = std::find_if(udpTransports_.cbegin(), udpTransports_.cend(), [tp](const std::pair<SipTransportDescr, pjsip_transport*>& i) { + return i.second == tp; + }); + if (transport_key != udpTransports_.end()) { + transports_.erase(transport_key->second); + udpTransports_.erase(transport_key); + transportDestroyedCv_.notify_all(); + } } + } +} - WARN("Transport was destroyed: {%s} %.*s", - tp->info, - info && description.slen > 0 ? description.slen : 0, err_msg); - transportMap_.erase(transport_key++); - transportDestroyedCv_.notify_all(); +std::shared_ptr<SipTransport> +SipTransportBroker::findTransport(pjsip_transport* t) +{ + if (!t) + return nullptr; + { + std::lock_guard<std::mutex> lock(transportMapMutex_); + auto i = transports_.find(t); + if (i == transports_.end()) { + auto ret = std::make_shared<SipTransport>(t); + transports_[t] = ret; + return ret; + } + else if (auto spt = i->second.lock()) + return spt; + else + return nullptr; } } void -SipTransport::waitForReleased(pjsip_transport* tp, std::function<void(bool)> released_cb) +SipTransportBroker::waitForReleased(const SipTransportDescr& tp, std::function<void(bool)> released_cb) { - if (!released_cb) - return; - if (!tp) { - released_cb(true); - return; - } - std::vector<pjsip_transport*> to_destroy_all; + std::vector<std::pair<SipTransportDescr, pjsip_transport*>> to_destroy_all; bool destroyed = false; { std::unique_lock<std::mutex> lock(transportMapMutex_); auto check_destroyed = [&](){ - std::vector<pjsip_transport*> to_destroy = _cleanupTransports(); - bool _destr = false; - for (auto t : to_destroy) { - if (t == tp) { - _destr = true; - break; - } - } - to_destroy_all.insert(to_destroy_all.end(), to_destroy.begin(), to_destroy.end()); - return _destr; + return udpTransports_.find(tp) == udpTransports_.end(); }; - destroyed = transportDestroyedCv_.wait_for(lock, std::chrono::milliseconds(50), check_destroyed); + destroyed = transportDestroyedCv_.wait_for(lock, std::chrono::seconds(10), check_destroyed); if (!destroyed) destroyed = check_destroyed(); } - for (auto t : to_destroy_all) { - pj_lock_release(t->lock); - pjsip_transport_destroy(t); - } - released_cb(destroyed); + + if (released_cb) + released_cb(destroyed); } -void -SipTransport::createSipTransport(SIPAccountBase &account) +std::shared_ptr<SipTransport> +SipTransportBroker::getUdpTransport(const SipTransportDescr& descr) { - // Remove any existing transport from the account - account.setTransport(); - cleanupTransports(); - - auto type = account.getTransportType(); - auto family = pjsip_transport_type_get_af(type); - auto interface = account.getLocalInterface(); - pjsip_transport* new_transport = nullptr; - - { - std::lock_guard<std::mutex> lock(transportMapMutex_); - std::string key; -#if HAVE_TLS - if (account.isTlsEnabled()) { - key = transportMapKey(interface, account.getTlsListenerPort(), type); - if (transportMap_.find(key) != transportMap_.end()) { - throw std::runtime_error("TLS transport already exists"); - } - createTlsTransport(account); - } else { -#else - { -#endif - auto port = account.getLocalPort(); - key = transportMapKey(interface, port, type); - // if this transport already exists, reuse it - auto iter = transportMap_.find(key); - if (iter != transportMap_.end()) { - auto status = pjsip_transport_add_ref(iter->second); - if (status == PJ_SUCCESS) - account.setTransport(iter->second); + std::lock_guard<std::mutex> lock(transportMapMutex_); + auto itp = udpTransports_.find(descr); + if (itp != udpTransports_.end()) { + auto it = transports_.find(itp->second); + if (it != transports_.end()) { + if (auto spt = it->second.lock()) { + DEBUG("Reusing transport %s", descr.toString().c_str()); + return spt; } - if (!account.getTransport()) { - account.setTransport(createUdpTransport(interface, port, family)); + else { + // Transport still exists but have not been destroyed yet. + WARN("Recycling transport %s", descr.toString().c_str()); + auto ret = std::make_shared<SipTransport>(itp->second); + it->second = ret; + return ret; } + } else { + WARN("Cleaning up UDP transport %s", descr.toString().c_str()); + udpTransports_.erase(itp); } - - new_transport = account.getTransport(); - if (new_transport) - transportMap_[key] = new_transport; } - cleanupTransports(); - - if (!new_transport) { -#if HAVE_TLS - if (account.isTlsEnabled()) - throw std::runtime_error("Could not create TLS connection"); - else -#endif - throw std::runtime_error("Could not create new UDP transport"); + auto ret = createUdpTransport(descr); + if (ret) { + udpTransports_[descr] = ret->get(); + transports_[ret->get()] = ret; } + return ret; } -pjsip_transport * -SipTransport::createUdpTransport(const std::string &interface, pj_uint16_t port, pj_uint16_t family) +std::shared_ptr<SipTransport> +SipTransportBroker::createUdpTransport(const SipTransportDescr& d) { - IpAddr listeningAddress; - if (interface == ip_utils::DEFAULT_INTERFACE) - listeningAddress = ip_utils::getAnyHostAddr(family); - else - listeningAddress = ip_utils::getInterfaceAddr(interface, family); + RETURN_IF_FAIL(d.listenerPort != 0, nullptr, "Could not determine port for this transport"); + auto family = pjsip_transport_type_get_af(d.type); - RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine ip address for this transport"); - RETURN_IF_FAIL(port != 0, nullptr, "Could not determine port for this transport"); + IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ? + ip_utils::getAnyHostAddr(family) : + ip_utils::getInterfaceAddr(d.interface, family); + listeningAddress.setPort(d.listenerPort); - listeningAddress.setPort(port); - pj_status_t status; + RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport"); pjsip_transport *transport = nullptr; - - if (listeningAddress.isIpv4()) { - status = pjsip_udp_transport_start(endpt_, &static_cast<const pj_sockaddr_in&>(listeningAddress), nullptr, 1, &transport); - if (status != PJ_SUCCESS) { - ERROR("UDP IPV4 Transport did not start"); - sip_utils::sip_strerror(status); - return nullptr; - } - } else if (listeningAddress.isIpv6()) { - status = pjsip_udp_transport_start6(endpt_, &static_cast<const pj_sockaddr_in6&>(listeningAddress), nullptr, 1, &transport); - if (status != PJ_SUCCESS) { - ERROR("UDP IPV6 Transport did not start"); - sip_utils::sip_strerror(status); - return nullptr; - } + pj_status_t status = listeningAddress.isIpv4() + ? pjsip_udp_transport_start (endpt_, &static_cast<const pj_sockaddr_in&>(listeningAddress), nullptr, 1, &transport) + : pjsip_udp_transport_start6(endpt_, &static_cast<const pj_sockaddr_in6&>(listeningAddress), nullptr, 1, &transport); + if (status != PJ_SUCCESS) { + ERROR("UDP IPv%s Transport did not start on %s", + listeningAddress.isIpv4() ? "4" : "6", + listeningAddress.toString(true).c_str()); + sip_utils::sip_strerror(status); + return nullptr; } - DEBUG("Created UDP transport on %s : %s", interface.c_str(), listeningAddress.toString(true).c_str()); - // dump debug information to stdout - //pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); - return transport; + DEBUG("Created UDP transport on %s : %s", d.interface.c_str(), listeningAddress.toString(true).c_str()); + auto ret = std::make_shared<SipTransport>(transport); + // dec ref because the refcount starts at 1 and SipTransport increments it ? + // pjsip_transport_dec_ref(transport); + return ret; } #if HAVE_TLS -pjsip_tpfactory* -SipTransport::createTlsListener(SIPAccountBase &account, pj_uint16_t family) +std::shared_ptr<TlsListener> +SipTransportBroker::getTlsListener(const SipTransportDescr& d, const pjsip_tls_setting* settings) { - RETURN_IF_FAIL(account.getTlsSetting() != nullptr, nullptr, "TLS settings not specified"); - - std::string interface(account.getLocalInterface()); - IpAddr listeningAddress; - if (interface == ip_utils::DEFAULT_INTERFACE) - listeningAddress = ip_utils::getAnyHostAddr(family); - else - listeningAddress = ip_utils::getInterfaceAddr(interface, family); + RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified"); + auto family = pjsip_transport_type_get_af(d.type); - listeningAddress.setPort(account.getTlsListenerPort()); + IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ? + ip_utils::getAnyHostAddr(family) : + ip_utils::getInterfaceAddr(d.interface, family); + listeningAddress.setPort(d.listenerPort); RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport"); - - DEBUG("Creating Listener on %s...", listeningAddress.toString(true).c_str()); - DEBUG("CRT file : %s", account.getTlsSetting()->ca_list_file.ptr); - DEBUG("PEM file : %s", account.getTlsSetting()->cert_file.ptr); + DEBUG("Creating TLS listener %s on %s...", d.toString().c_str(), listeningAddress.toString(true).c_str()); + DEBUG(" ca_list_file : %s", settings->ca_list_file.ptr); + DEBUG(" cert_file : %s", settings->cert_file.ptr); pjsip_tpfactory *listener = nullptr; - const pj_status_t status = pjsip_tls_transport_start2(endpt_, account.getTlsSetting(), listeningAddress.pjPtr(), nullptr, 1, &listener); + const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, listeningAddress.pjPtr(), nullptr, 1, &listener); if (status != PJ_SUCCESS) { ERROR("TLS listener did not start"); sip_utils::sip_strerror(status); return nullptr; } - return listener; + return std::make_shared<TlsListener>(listener); } -pjsip_transport * -SipTransport::createTlsTransport(SIPAccountBase &account) +std::shared_ptr<SipTransport> +SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l, const std::string& remoteSipUri) { - std::string remoteSipUri(account.getServerUri()); + if (!l) { + ERROR("Can't create TLS transport without listener."); + return nullptr; + } static const char SIPS_PREFIX[] = "<sips:"; size_t sips = remoteSipUri.find(SIPS_PREFIX) + (sizeof SIPS_PREFIX) - 1; size_t trns = remoteSipUri.find(";transport"); IpAddr remoteAddr = {remoteSipUri.substr(sips, trns-sips)}; if (!remoteAddr) return nullptr; - - const pjsip_transport_type_e transportType = -#if HAVE_IPV6 - remoteAddr.isIpv6() ? PJSIP_TRANSPORT_TLS6 : -#endif - PJSIP_TRANSPORT_TLS; - - int port = pjsip_transport_get_default_port_for_type(transportType); if (remoteAddr.getPort() == 0) - remoteAddr.setPort(port); - - DEBUG("Get new tls transport/listener from transport manager to %s", remoteAddr.toString(true).c_str()); - - // create listener - pjsip_tpfactory *localTlsListener = createTlsListener(account, remoteAddr.getFamily()); + remoteAddr.setPort(pjsip_transport_get_default_port_for_type(l->get()->type)); - // create transport + DEBUG("Get new TLS transport to %s", remoteAddr.toString(true).c_str()); + pjsip_tpselector sel {PJSIP_TPSELECTOR_LISTENER, { + .listener = l->get() + }}; pjsip_transport *transport = nullptr; pj_status_t status = pjsip_endpt_acquire_transport( endpt_, - transportType, + l->get()->type, remoteAddr.pjPtr(), - pj_sockaddr_get_len(remoteAddr.pjPtr()), - nullptr, + remoteAddr.getLength(), + &sel, &transport); if (!transport || status != PJ_SUCCESS) { - if (localTlsListener) - localTlsListener->destroy(localTlsListener); - ERROR("Could not create new TLS transport"); + ERROR("Could not get new TLS transport"); sip_utils::sip_strerror(status); return nullptr; } - - account.setTransport(transport, localTlsListener); - return transport; -} -#endif - -void -SipTransport::cleanupTransports() -{ - std::vector<pjsip_transport*> to_destroy; + auto ret = std::make_shared<SipTransport>(transport, l); + pjsip_transport_dec_ref(transport); { std::lock_guard<std::mutex> lock(transportMapMutex_); - to_destroy = _cleanupTransports(); - } - for (auto t : to_destroy) { - pj_lock_release(t->lock); - pjsip_transport_destroy(t); + transports_[ret->get()] = ret; } + return ret; } - -std::vector<pjsip_transport*> -SipTransport::_cleanupTransports() -{ - std::vector<pjsip_transport*> to_destroy; - for (auto it = transportMap_.cbegin(); it != transportMap_.cend();) { - pjsip_transport* t = (*it).second; - if (!t) { - transportMap_.erase(it++); - continue; - } - pj_lock_acquire(t->lock); - auto ref_cnt = pj_atomic_get(t->ref_cnt); - if (ref_cnt == 0 || t->is_shutdown || t->is_destroying) { - DEBUG("Removing transport for %s", t->info ); - bool is_shutdown = t->is_shutdown || t->is_destroying; - transportMap_.erase(it++); - if (!is_shutdown) - to_destroy.push_back(t); - else - pj_lock_release(t->lock); - } else { - ++it; - pj_lock_release(t->lock); - } - } - return to_destroy; -} +#endif std::vector<pj_sockaddr> -SipTransport::getSTUNAddresses(const SIPAccountBase &account, - std::vector<long> &socketDescriptors) const +SipTransportBroker::getSTUNAddresses(const SIPAccountBase &account, std::vector<long> &socketDescriptors) const { const pj_str_t serverName = account.getStunServerName(); const pj_uint16_t port = account.getStunPort(); @@ -407,7 +390,8 @@ SipTransport::getSTUNAddresses(const SIPAccountBase &account, #define RETURN_IF_NULL(A, M, ...) if ((A) == NULL) { ERROR(M, ##__VA_ARGS__); return; } -void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, const std::string &host, std::string &addr, pj_uint16_t &port) const +void +SipTransportBroker::findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, const std::string &host, std::string &addr, pj_uint16_t &port) const { // Initialize the sip port with the default SIP port port = pjsip_transport_get_default_port_for_type(transportType); @@ -440,7 +424,7 @@ void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjs } void -SipTransport::findLocalAddressFromSTUN(pjsip_transport *transport, +SipTransportBroker::findLocalAddressFromSTUN(pjsip_transport *transport, pj_str_t *stunServerName, int stunPort, std::string &addr, pj_uint16_t &port) const diff --git a/daemon/src/sip/siptransport.h b/daemon/src/sip/siptransport.h index 3c87eaf1b1..20f63593ee 100644 --- a/daemon/src/sip/siptransport.h +++ b/daemon/src/sip/siptransport.h @@ -40,10 +40,6 @@ #include "logger.h" #include <pjsip.h> -#include <pjsip_ua.h> -#include <pjlib.h> -#include <pjlib-util.h> -#include <pjnath.h> #include <pjnath/stun_config.h> #include <functional> @@ -59,118 +55,193 @@ class SIPAccountBase; +struct SipTransportDescr +{ + SipTransportDescr() {} + SipTransportDescr(pjsip_transport_type_e t) + : type(t), listenerPort(pjsip_transport_get_default_port_for_type(t)) {} + SipTransportDescr(pjsip_transport_type_e t, pj_uint16_t port, std::string i) + : type(t), listenerPort(port), interface(i) {} + + static inline pjsip_transport_type_e actualType(pjsip_transport_type_e t) { + return (t == PJSIP_TRANSPORT_START_OTHER) ? PJSIP_TRANSPORT_UDP : t; + } + + inline bool operator==(SipTransportDescr const& o) const { + return actualType(type) == actualType(o.type) + && listenerPort == o.listenerPort + && interface == o.interface; + } + + inline bool operator<(SipTransportDescr const& o) const { + return actualType(type) < actualType(o.type) + || listenerPort < o.listenerPort + || std::hash<std::string>()(interface) < std::hash<std::string>()(o.interface); + } + + std::string toString() const; + + pjsip_transport_type_e type {PJSIP_TRANSPORT_UNSPECIFIED}; + pj_uint16_t listenerPort {DEFAULT_SIP_PORT}; + std::string interface {"default"}; +}; + +struct SipTransport; + +struct TlsListener +{ + TlsListener() {} + TlsListener(pjsip_tpfactory* f) : listener(f) {} + virtual ~TlsListener() { + DEBUG("Destroying listener"); + listener->destroy(listener); + } + pjsip_tpfactory* get() { + return listener; + } +private: + NON_COPYABLE(TlsListener); + pjsip_tpfactory* listener {nullptr}; +}; + +typedef std::function<void(pjsip_transport_state, const pjsip_transport_state_info*)> SipTransportStateCallback; -class SipTransport { - public: - SipTransport(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool); - ~SipTransport(); - - /** - * General Sip transport creation method according to the - * transport type specified in account settings - * @param account The account for which a transport must be created. - */ - void createSipTransport(SIPAccountBase &account); - - /** - * Initialize the transport selector - * @param transport A transport associated with an account - * @return A transport selector structure - */ - static inline pjsip_tpselector getTransportSelector(pjsip_transport *transport) { - pjsip_tpselector tp = {PJSIP_TPSELECTOR_TRANSPORT, {transport}}; - return tp; +struct SipTransport +{ + SipTransport() {} + SipTransport(pjsip_transport*, const std::shared_ptr<TlsListener>& l = {}); + + virtual ~SipTransport(); + + static const char* stateToStr(pjsip_transport_state state); + + void stateCallback(pjsip_transport_state state, const pjsip_transport_state_info *info) { + std::vector<SipTransportStateCallback> cbs {}; + { + std::lock_guard<std::mutex> lock(stateListenersMutex_); + cbs.reserve(stateListeners.size()); + for (auto& l : stateListeners) + cbs.push_back(l.second); + } + for (auto& cb : cbs) + cb(state, info); + } + + pjsip_transport* get() { + return transport; + } + + void addStateListener(uintptr_t lid, SipTransportStateCallback cb) { + std::lock_guard<std::mutex> lock(stateListenersMutex_); + stateListeners[lid] = cb; + } + + bool removeStateListener(uintptr_t lid) { + std::lock_guard<std::mutex> lock(stateListenersMutex_); + auto it = stateListeners.find(lid); + if (it != stateListeners.end()) { + stateListeners.erase(it); + return true; } + return false; + } - /** - * This function returns a list of STUN mapped sockets for - * a given set of socket file descriptors */ - std::vector<pj_sockaddr> getSTUNAddresses(const SIPAccountBase &account, std::vector<long> &socks) const; - - /** - * Get the correct address to use (ie advertised) from - * a uri. The corresponding transport that should be used - * with that uri will be discovered. - * - * @param uri The uri from which we want to discover the address to use - * @param transport The transport to use to discover the address - */ - void findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, const std::string &host, std::string &address, pj_uint16_t &port) const; - - void findLocalAddressFromSTUN(pjsip_transport *transport, pj_str_t *stunServerName, - int stunPort, std::string &address, pj_uint16_t &port) const; - - /** - * Go through the transport list and remove unused ones. - */ - void cleanupTransports(); - - /** - * Call released_cb(success) when transport tp is destroyed, making the - * socket available for a new similar transport. - * success is true if the transport is actually released. - * TODO: make this call non-blocking. - */ - void waitForReleased(pjsip_transport* tp, std::function<void(bool)> released_cb); - - private: - NON_COPYABLE(SipTransport); + static bool isAlive(const std::shared_ptr<SipTransport>&, pjsip_transport_state state); + +private: + NON_COPYABLE(SipTransport); + pjsip_transport* transport {nullptr}; + std::shared_ptr<TlsListener> tlsListener {}; + std::map<uintptr_t, SipTransportStateCallback> stateListeners {}; + std::mutex stateListenersMutex_ {}; +}; + +/** + * Manages the transports and receive callbacks from PJSIP + */ +class SipTransportBroker { +public: + SipTransportBroker(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool); + ~SipTransportBroker(); + + std::shared_ptr<SipTransport> getUdpTransport(const SipTransportDescr&); #if HAVE_TLS - /** - * Create a connection oriented TLS transport and register to the specified remote address. - * First, initialize the TLS listener sole instance. This means that, for the momment, only one TLS transport - * is allowed to be created in the application. Any subsequent account attempting to - * register a new using this transport even if new settings are specified. - * @param the account that is creating the TLS transport - */ - pjsip_transport * - createTlsTransport(SIPAccountBase &account); - - /** - * Create The default TLS listener which is global to the application. This means that - * only one TLS connection can be established for the momment. - * @param the SIPAccount for which we are creating the TLS listener - * @param IP protocol version to use, can be pj_AF_INET() or pj_AF_INET6() - * @return a pointer to the new listener - */ - pjsip_tpfactory * - createTlsListener(SIPAccountBase &account, pj_uint16_t family = pj_AF_UNSPEC()); + std::shared_ptr<TlsListener> getTlsListener(const SipTransportDescr&, const pjsip_tls_setting*); + + std::shared_ptr<SipTransport> getTlsTransport(const std::shared_ptr<TlsListener>&, const std::string& remoteSipUri); #endif - /** - * Create SIP UDP transport from account's setting - * @param account The account for which a transport must be created. - * @param IP protocol version to use, can be pj_AF_INET() or pj_AF_INET6() - * @return a pointer to the new transport - */ - pjsip_transport *createUdpTransport(const std::string &interface, - pj_uint16_t port, pj_uint16_t family = pj_AF_UNSPEC()); - - /** - * Go through the transport list and remove unused ones. - * Returns a list of LOCKED transports that have to be processed and unlocked. - */ - std::vector<pjsip_transport*> _cleanupTransports(); - - static void tp_state_callback(pjsip_transport *, pjsip_transport_state, const pjsip_transport_state_info *); - - void transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info); - - /** - * UDP Transports are stored in this map in order to retreive them in case - * several accounts would share the same port number. - */ - std::map<std::string, pjsip_transport*> transportMap_; - std::mutex transportMapMutex_; - std::condition_variable transportDestroyedCv_; - - pj_caching_pool& cp_; - pj_pool_t& pool_; - - pjsip_endpoint *endpt_; + std::shared_ptr<SipTransport> findTransport(pjsip_transport*); + + /** + * Initialize the transport selector + * @param transport A transport associated with an account + * @return A transport selector structure + */ + static inline pjsip_tpselector getTransportSelector(pjsip_transport *transport) { + pjsip_tpselector tp = {PJSIP_TPSELECTOR_TRANSPORT, {transport}}; + return tp; + } + + /** + * This function returns a list of STUN mapped sockets for + * a given set of socket file descriptors */ + std::vector<pj_sockaddr> getSTUNAddresses(const SIPAccountBase &account, std::vector<long> &socks) const; + + /** + * Get the correct address to use (ie advertised) from + * a uri. The corresponding transport that should be used + * with that uri will be discovered. + * + * @param uri The uri from which we want to discover the address to use + * @param transport The transport to use to discover the address + */ + void findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, const std::string &host, std::string &address, pj_uint16_t &port) const; + + void findLocalAddressFromSTUN(pjsip_transport *transport, pj_str_t *stunServerName, + int stunPort, std::string &address, pj_uint16_t &port) const; + + /** + * Call released_cb(success) when transport tp is destroyed, making the + * socket available for a new similar transport. + * success is true if the transport is actually released. + * TODO: make this call non-blocking. + */ + void waitForReleased(const SipTransportDescr& tp, std::function<void(bool)> released_cb); + +private: + NON_COPYABLE(SipTransportBroker); + + /** + * Create SIP UDP transport from account's setting + * @param account The account for which a transport must be created. + * @param IP protocol version to use, can be pj_AF_INET() or pj_AF_INET6() + * @return a pointer to the new transport + */ + std::shared_ptr<SipTransport> createUdpTransport(const SipTransportDescr&); + + static void tp_state_callback(pjsip_transport*, pjsip_transport_state, const pjsip_transport_state_info*); + + void transportStateChanged(pjsip_transport*, pjsip_transport_state, const pjsip_transport_state_info*); + + /** + * List of transports so we can bubble the events up. + */ + std::map<pjsip_transport*, std::weak_ptr<SipTransport>> transports_ {}; + + /** + * Transports are stored in this map in order to retreive them in case + * several accounts would share the same port number. + */ + std::map<SipTransportDescr, pjsip_transport*> udpTransports_ {}; + + std::mutex transportMapMutex_ {}; + std::condition_variable transportDestroyedCv_ {}; + + pj_caching_pool& cp_; + pj_pool_t& pool_; + pjsip_endpoint *endpt_; }; -void sip_strerror(pj_status_t code); - #endif // SIPTRANSPORT_H_ diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index b5ba5624c2..cf20cd446c 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -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()); - } -} diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h index 209cdf3b54..ca1cfca82a 100644 --- a/daemon/src/sip/sipvoiplink.h +++ b/daemon/src/sip/sipvoiplink.h @@ -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_; -- GitLab