LCOV - code coverage report
Current view: top level - foo/src/sip - siptransport.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 131 204 64.2 %
Date: 2025-12-18 10:07:43 Functions: 34 59 57.6 %

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

Generated by: LCOV version 1.14