LCOV - code coverage report
Current view: top level - src/sip - siptransport.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 63.7 % 204 130
Test Date: 2026-06-13 09:18:46 Functions: 57.6 % 59 34

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #include "sip/siptransport.h"
      19              : #include "connectivity/sip_utils.h"
      20              : 
      21              : #include "jamidht/channeled_transport.h"
      22              : 
      23              : #include "sip/sipvoiplink.h"
      24              : 
      25              : #include <pjsip.h>
      26              : #include <pjsip/sip_types.h>
      27              : #include <pjsip/sip_transport_tls.h>
      28              : #include <pj/ssl_sock.h>
      29              : #include <pjnath.h>
      30              : #include <pjnath/stun_config.h>
      31              : #include <pjlib.h>
      32              : #include <pjlib-util.h>
      33              : 
      34              : #include <dhtnet/multiplexed_socket.h>
      35              : #include <dhtnet/ip_utils.h>
      36              : #include <dhtnet/tls_session.h>
      37              : 
      38              : #include <opendht/crypto.h>
      39              : 
      40              : #include <stdexcept>
      41              : #include <algorithm>
      42              : 
      43              : #define RETURN_IF_FAIL(A, VAL, ...) \
      44              :     if (!(A)) { \
      45              :         JAMI_ERROR(__VA_ARGS__); \
      46              :         return (VAL); \
      47              :     }
      48              : 
      49              : namespace jami {
      50              : 
      51              : constexpr const char* TRANSPORT_STATE_STR[] = {"CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE"};
      52              : constexpr const size_t TRANSPORT_STATE_SZ = std::size(TRANSPORT_STATE_STR);
      53              : 
      54              : void
      55          198 : SipTransport::deleteTransport(pjsip_transport* t)
      56              : {
      57          198 :     pjsip_transport_dec_ref(t);
      58          198 : }
      59              : 
      60          197 : SipTransport::SipTransport(pjsip_transport* t)
      61          197 :     : transport_(nullptr)
      62              : {
      63          198 :     if (not t or pjsip_transport_add_ref(t) != PJ_SUCCESS)
      64            0 :         throw std::runtime_error("Invalid transport");
      65              : 
      66              :     // Set pointer here, right after the successful pjsip_transport_add_ref
      67          198 :     transport_.reset(t);
      68              : 
      69          792 :     JAMI_DEBUG("SipTransport@{} tr={} rc={:d}",
      70              :                fmt::ptr(this),
      71              :                fmt::ptr(transport_.get()),
      72              :                pj_atomic_get(transport_->ref_cnt));
      73          198 : }
      74              : 
      75            0 : SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l)
      76            0 :     : SipTransport(t)
      77              : {
      78            0 :     tlsListener_ = l;
      79            0 : }
      80              : 
      81          173 : SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<dht::crypto::Certificate>& peerCertficate)
      82          173 :     : SipTransport(t)
      83              : {
      84          174 :     tlsInfos_.peerCert = peerCertficate;
      85          174 : }
      86              : 
      87          198 : SipTransport::~SipTransport()
      88              : {
      89          792 :     JAMI_DEBUG("~SipTransport@{} tr={} rc={:d}",
      90              :                fmt::ptr(this),
      91              :                fmt::ptr(transport_.get()),
      92              :                pj_atomic_get(transport_->ref_cnt));
      93          198 : }
      94              : 
      95              : bool
      96           16 : SipTransport::isAlive(pjsip_transport_state state)
      97              : {
      98           16 :     return state != PJSIP_TP_STATE_DISCONNECTED && state != PJSIP_TP_STATE_SHUTDOWN && state != PJSIP_TP_STATE_DESTROY;
      99              : }
     100              : 
     101              : const char*
     102          332 : SipTransport::stateToStr(pjsip_transport_state state)
     103              : {
     104          332 :     return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ - 1)];
     105              : }
     106              : 
     107              : void
     108          102 : SipTransport::stateCallback(pjsip_transport_state state, const pjsip_transport_state_info* info)
     109              : {
     110          102 :     connected_ = state == PJSIP_TP_STATE_CONNECTED;
     111              : 
     112          102 :     const auto* extInfo = static_cast<const pjsip_tls_state_info*>(info->ext_info);
     113          102 :     if (isSecure() && extInfo && extInfo->ssl_sock_info && extInfo->ssl_sock_info->established) {
     114            0 :         auto* tlsInfo = extInfo->ssl_sock_info;
     115            0 :         tlsInfos_.proto = (pj_ssl_sock_proto) tlsInfo->proto;
     116            0 :         tlsInfos_.cipher = tlsInfo->cipher;
     117            0 :         tlsInfos_.verifyStatus = (pj_ssl_cert_verify_flag_t) tlsInfo->verify_status;
     118            0 :         if (!tlsInfos_.peerCert) {
     119            0 :             const auto& peers = tlsInfo->remote_cert_info->raw_chain;
     120            0 :             std::vector<std::pair<const uint8_t*, const uint8_t*>> bits;
     121            0 :             bits.resize(peers.cnt);
     122            0 :             std::transform(peers.cert_raw, peers.cert_raw + peers.cnt, std::begin(bits), [](const pj_str_t& crt) {
     123            0 :                 return std::make_pair((uint8_t*) crt.ptr, (uint8_t*) (crt.ptr + crt.slen));
     124              :             });
     125            0 :             tlsInfos_.peerCert = std::make_shared<dht::crypto::Certificate>(bits);
     126            0 :         }
     127              :     } else {
     128          102 :         tlsInfos_ = {};
     129              :     }
     130              : 
     131          102 :     std::vector<SipTransportStateCallback> cbs;
     132              :     {
     133          102 :         std::lock_guard lock(stateListenersMutex_);
     134          102 :         cbs.reserve(stateListeners_.size());
     135          218 :         for (auto& l : stateListeners_)
     136          116 :             cbs.push_back(l.second);
     137          102 :     }
     138          218 :     for (auto& cb : cbs)
     139          116 :         cb(state, info);
     140          204 : }
     141              : 
     142              : void
     143          301 : SipTransport::addStateListener(uintptr_t lid, const SipTransportStateCallback& cb)
     144              : {
     145          301 :     std::lock_guard lock(stateListenersMutex_);
     146          301 :     auto pair = stateListeners_.insert(std::make_pair(lid, cb));
     147          301 :     if (not pair.second)
     148            1 :         pair.first->second = cb;
     149          301 : }
     150              : 
     151              : bool
     152          299 : SipTransport::removeStateListener(uintptr_t lid)
     153              : {
     154          299 :     std::lock_guard lock(stateListenersMutex_);
     155          299 :     auto it = stateListeners_.find(lid);
     156          299 :     if (it != stateListeners_.end()) {
     157           25 :         stateListeners_.erase(it);
     158           25 :         return true;
     159              :     }
     160          274 :     return false;
     161          299 : }
     162              : 
     163              : uint16_t
     164          318 : SipTransport::getTlsMtu()
     165              : {
     166          318 :     return 1232; /* Hardcoded yes (it's the IPv6 value).
     167              :                   * This method is broken by definition.
     168              :                   * A MTU should not be defined at this layer.
     169              :                   * And a correct value should come from the underlying transport itself,
     170              :                   * not from a constant…
     171              :                   */
     172              : }
     173              : 
     174           32 : SipTransportBroker::SipTransportBroker(pjsip_endpoint* endpt)
     175           32 :     : endpt_(endpt)
     176           32 : {}
     177              : 
     178           32 : SipTransportBroker::~SipTransportBroker()
     179              : {
     180           32 :     shutdown();
     181              : 
     182           32 :     udpTransports_.clear();
     183           32 :     transports_.clear();
     184              : 
     185          128 :     JAMI_DEBUG("Destroying SipTransportBroker@{}…", fmt::ptr(this));
     186           32 : }
     187              : 
     188              : void
     189          332 : SipTransportBroker::transportStateChanged(pjsip_transport* tp,
     190              :                                           pjsip_transport_state state,
     191              :                                           const pjsip_transport_state_info* info)
     192              : {
     193         1328 :     JAMI_DEBUG("PJSIP transport@{} {} → {}", fmt::ptr(tp), tp->info, SipTransport::stateToStr(state));
     194              : 
     195              :     // First ensure that this transport is handled by us
     196              :     // and remove it from any mapping if destroy pending or done.
     197              : 
     198          332 :     std::shared_ptr<SipTransport> sipTransport;
     199          332 :     std::lock_guard lock(transportMapMutex_);
     200          332 :     auto key = transports_.find(tp);
     201          332 :     if (key == transports_.end())
     202            0 :         return;
     203              : 
     204          332 :     sipTransport = key->second.lock();
     205              : 
     206          332 :     if (!isDestroying_ && state == PJSIP_TP_STATE_DESTROY) {
     207              :         // maps cleanup
     208          624 :         JAMI_DEBUG("Unmap PJSIP transport@{} {{SipTransport@{}}}", fmt::ptr(tp), fmt::ptr(sipTransport.get()));
     209          156 :         transports_.erase(key);
     210              : 
     211              :         // If UDP
     212          156 :         const auto type = tp->key.type;
     213          156 :         if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
     214            0 :             const auto updKey = std::find_if(udpTransports_.cbegin(),
     215              :                                              udpTransports_.cend(),
     216            0 :                                              [tp](const std::pair<dhtnet::IpAddr, pjsip_transport*>& pair) {
     217            0 :                                                  return pair.second == tp;
     218              :                                              });
     219            0 :             if (updKey != udpTransports_.cend())
     220            0 :                 udpTransports_.erase(updKey);
     221              :         }
     222              :     }
     223              : 
     224              :     // Propagate the event to the appropriate transport
     225              :     // Note the SipTransport may not be in our mappings if marked as dead
     226          332 :     if (sipTransport)
     227          102 :         sipTransport->stateCallback(state, info);
     228          332 : }
     229              : 
     230              : std::shared_ptr<SipTransport>
     231          106 : SipTransportBroker::addTransport(pjsip_transport* t)
     232              : {
     233          106 :     if (t) {
     234          106 :         std::lock_guard lock(transportMapMutex_);
     235              : 
     236          106 :         auto key = transports_.find(t);
     237          106 :         if (key != transports_.end()) {
     238          106 :             if (auto sipTr = key->second.lock())
     239          106 :                 return sipTr;
     240              :         }
     241              : 
     242            0 :         auto sipTr = std::make_shared<SipTransport>(t);
     243            0 :         if (key != transports_.end())
     244            0 :             key->second = sipTr;
     245              :         else
     246            0 :             transports_.emplace(std::make_pair(t, sipTr));
     247            0 :         return sipTr;
     248            0 :     }
     249              : 
     250            0 :     return nullptr;
     251              : }
     252              : 
     253              : void
     254           64 : SipTransportBroker::shutdown()
     255              : {
     256           64 :     std::unique_lock lock(transportMapMutex_);
     257           64 :     isDestroying_ = true;
     258          118 :     for (auto& t : transports_) {
     259           54 :         if (auto transport = t.second.lock()) {
     260            0 :             pjsip_transport_shutdown(transport->get());
     261           54 :         }
     262              :     }
     263           64 : }
     264              : 
     265              : std::shared_ptr<SipTransport>
     266           28 : SipTransportBroker::getUdpTransport(const dhtnet::IpAddr& ipAddress)
     267              : {
     268           28 :     std::lock_guard lock(transportMapMutex_);
     269           28 :     auto itp = udpTransports_.find(ipAddress);
     270           28 :     if (itp != udpTransports_.end()) {
     271           19 :         auto it = transports_.find(itp->second);
     272           19 :         if (it != transports_.end()) {
     273           19 :             if (auto spt = it->second.lock()) {
     274           16 :                 JAMI_DEBUG("Reusing transport {}", ipAddress.toString(true));
     275            4 :                 return spt;
     276              :             } else {
     277              :                 // Transport still exists but have not been destroyed yet.
     278           60 :                 JAMI_WARNING("Recycling transport {}", ipAddress.toString(true));
     279           15 :                 auto ret = std::make_shared<SipTransport>(itp->second);
     280           15 :                 it->second = ret;
     281           15 :                 return ret;
     282           34 :             }
     283              :         } else {
     284            0 :             JAMI_WARNING("Cleaning up UDP transport {}", ipAddress.toString(true));
     285            0 :             udpTransports_.erase(itp);
     286              :         }
     287              :     }
     288            9 :     auto ret = createUdpTransport(ipAddress);
     289            9 :     if (ret) {
     290            9 :         udpTransports_[ipAddress] = ret->get();
     291            9 :         transports_[ret->get()] = ret;
     292              :     }
     293            9 :     return ret;
     294           28 : }
     295              : 
     296              : std::shared_ptr<SipTransport>
     297            9 : SipTransportBroker::createUdpTransport(const dhtnet::IpAddr& ipAddress)
     298              : {
     299            9 :     RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
     300              : 
     301              :     pjsip_udp_transport_cfg pj_cfg;
     302            9 :     pjsip_udp_transport_cfg_default(&pj_cfg, ipAddress.getFamily());
     303            9 :     pj_cfg.bind_addr = ipAddress;
     304            9 :     pjsip_transport* transport = nullptr;
     305            9 :     if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
     306            0 :         JAMI_ERROR("pjsip_udp_transport_start2 failed with error {:d}: {:s}", status, sip_utils::sip_strerror(status));
     307            0 :         JAMI_ERROR("UDP IPv{} Transport did not start on {}", ipAddress.isIpv4() ? "4" : "6", ipAddress.toString(true));
     308            0 :         return nullptr;
     309              :     }
     310              : 
     311           36 :     JAMI_DEBUG("Created UDP transport on address {}", ipAddress.toString(true));
     312            9 :     return std::make_shared<SipTransport>(transport);
     313              : }
     314              : 
     315              : std::shared_ptr<TlsListener>
     316            0 : SipTransportBroker::getTlsListener(const dhtnet::IpAddr& ipAddress, const pjsip_tls_setting* settings)
     317              : {
     318            0 :     RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
     319            0 :     RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
     320            0 :     JAMI_DEBUG("Creating TLS listener on {:s}…", ipAddress.toString(true));
     321              : 
     322            0 :     pjsip_tpfactory* listener = nullptr;
     323            0 :     const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, ipAddress.pjPtr(), nullptr, 1, &listener);
     324            0 :     if (status != PJ_SUCCESS) {
     325            0 :         JAMI_ERROR("TLS listener did not start: {}", sip_utils::sip_strerror(status));
     326            0 :         return nullptr;
     327              :     }
     328            0 :     return std::make_shared<TlsListener>(listener);
     329              : }
     330              : 
     331              : std::shared_ptr<SipTransport>
     332            0 : SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l,
     333              :                                     const dhtnet::IpAddr& remote,
     334              :                                     const std::string& remote_name)
     335              : {
     336            0 :     if (!l || !remote)
     337            0 :         return nullptr;
     338            0 :     dhtnet::IpAddr remoteAddr {remote};
     339            0 :     if (remoteAddr.getPort() == 0)
     340            0 :         remoteAddr.setPort(pjsip_transport_get_default_port_for_type(l->get()->type));
     341              : 
     342            0 :     JAMI_DEBUG("Get new TLS transport to {}", remoteAddr.toString(true));
     343              :     pjsip_tpselector sel;
     344            0 :     sel.type = PJSIP_TPSELECTOR_LISTENER;
     345            0 :     sel.u.listener = l->get();
     346            0 :     sel.disable_connection_reuse = PJ_FALSE;
     347              : 
     348              :     pjsip_tx_data tx_data;
     349            0 :     tx_data.dest_info.name = pj_str_t {(char*) remote_name.data(), (pj_ssize_t) remote_name.size()};
     350              : 
     351            0 :     pjsip_transport* transport = nullptr;
     352            0 :     pj_status_t status = pjsip_endpt_acquire_transport2(endpt_,
     353            0 :                                                         l->get()->type,
     354            0 :                                                         remoteAddr.pjPtr(),
     355            0 :                                                         static_cast<int>(remoteAddr.getLength()),
     356              :                                                         &sel,
     357            0 :                                                         remote_name.empty() ? nullptr : &tx_data,
     358              :                                                         &transport);
     359              : 
     360            0 :     if (!transport || status != PJ_SUCCESS) {
     361            0 :         JAMI_ERROR("Unable to get new TLS transport: {}", sip_utils::sip_strerror(status));
     362            0 :         return nullptr;
     363              :     }
     364            0 :     auto ret = std::make_shared<SipTransport>(transport, l);
     365            0 :     pjsip_transport_dec_ref(transport);
     366              :     {
     367            0 :         std::lock_guard lock(transportMapMutex_);
     368            0 :         transports_[ret->get()] = ret;
     369            0 :     }
     370            0 :     return ret;
     371            0 : }
     372              : 
     373              : std::shared_ptr<SipTransport>
     374          174 : SipTransportBroker::getChanneledTransport(const std::shared_ptr<SIPAccountBase>& account,
     375              :                                           const std::shared_ptr<dhtnet::ChannelSocket>& socket,
     376              :                                           onShutdownCb&& cb)
     377              : {
     378          174 :     if (!socket)
     379            0 :         return {};
     380          174 :     auto sips_tr = std::make_unique<tls::ChanneledSIPTransport>(endpt_, socket, std::move(cb));
     381          174 :     auto* tr = sips_tr->getTransportBase();
     382          174 :     auto sip_tr = std::make_shared<SipTransport>(tr, socket->peerCertificate());
     383          174 :     sip_tr->setDeviceId(socket->deviceId().toString());
     384          174 :     sip_tr->setAccount(account);
     385              : 
     386              :     {
     387          174 :         std::lock_guard lock(transportMapMutex_);
     388              :         // we do not check for key existence as we've just created it
     389              :         // (member of new SipIceTransport instance)
     390          174 :         transports_.emplace(tr, sip_tr);
     391          174 :     }
     392              : 
     393          174 :     sips_tr->start();
     394              :     // NOLINTNEXTLINE: No value in capturing the released pointer
     395          174 :     sips_tr.release(); // managed by PJSIP now
     396          174 :     return sip_tr;
     397          174 : }
     398              : 
     399              : } // namespace jami
        

Generated by: LCOV version 2.0-1