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