Skip to content
Snippets Groups Projects
Select Git revision
  • abde3cbac085ac3990e9822b510abada8ca5c7ac
  • master default protected
  • nightly/20250714.0
  • beta/202507141552
  • beta/202506161038
  • stable/20250613.0
  • nightly/20250613.0
  • beta/202506101658
  • stable/20250610.0
  • nightly/20250610.0
  • beta/202506091027
  • beta/202506061543
  • nightly/20250605.0
  • beta/202506051039
  • beta/202506051002
  • beta/202506041611
  • beta/202506041335
  • beta/202505231812
  • stable/20250523.0
  • nightly/20250523.0
  • nightly/20250515.0
  • nightly/20250510.0
22 results

audiodevicemodel.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    siptransport.cpp 18.03 KiB
    /*
     *  Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
     *
     *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
     *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 3 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
     *
     *  Additional permission under GNU GPL version 3 section 7:
     *
     *  If you modify this program, or any covered work, by linking or
     *  combining it with the OpenSSL project's OpenSSL library (or a
     *  modified version of that library), containing parts covered by the
     *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
     *  grants you additional permission to convey the resulting work.
     *  Corresponding Source for a non-source form of such a combination
     *  shall include the source code for the parts of OpenSSL used as well
     *  as that of the covered work.
     */
    
    #include "siptransport.h"
    #include "sip_utils.h"
    #include "ip_utils.h"
    
    #include "ringdht/sip_transport_ice.h"
    
    #include "manager.h"
    #include "client/configurationmanager.h"
    #include "map_utils.h"
    #include "array_size.h"
    #include "intrin.h"
    
    #include <pjsip.h>
    #include <pjsip/sip_types.h>
    #if HAVE_TLS
    #include <pjsip/sip_transport_tls.h>
    #endif
    #include <pjnath.h>
    #include <pjnath/stun_config.h>
    #include <pjlib.h>
    #include <pjlib-util.h>
    
    #include <stdexcept>
    #include <sstream>
    #include <algorithm>
    
    #define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { SFL_ERR(M, ##__VA_ARGS__); return (VAL); }
    
    // 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 = SFL_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()
    {
        if (transport) {
            pjsip_transport_shutdown(transport);
            pjsip_transport_dec_ref(transport); // ??
            SFL_DBG("Destroying transport (refcount: %u)",  pj_atomic_get(transport->ref_cnt));
            transport = nullptr;
        }
    }
    
    bool
    SipTransport::isAlive(UNUSED 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) :
    iceTransports_(), 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) {
            SFL_ERR("Can't set transport callback");
            sip_utils::sip_strerror(status);
        }
    
        pjsip_transport_register_type(PJSIP_TRANSPORT_DATAGRAM, "ICE", pjsip_transport_get_default_port_for_type(PJSIP_TRANSPORT_UDP), &ice_pj_transport_type_);
    }
    
    SipTransportBroker::~SipTransportBroker()
    {
        instance = nullptr;
        pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), nullptr);
    }
    
    /** Static tranport state change callback */
    void
    SipTransportBroker::tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
    {
        if (!instance) {
            SFL_ERR("Can't bubble event: SipTransportBroker instance is null !");
            return;
        }
        instance->transportStateChanged(tp, state, info);
    }
    
    void
    SipTransportBroker::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
    {
        SFL_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_DESTROY) {
    #else
        if (tp->is_destroying) {
    #endif
            std::lock_guard<std::mutex> lock(transportMapMutex_);
    
            // Transport map cleanup
            auto t = transports_.find(tp);
            if (t != transports_.end() && t->second.expired())
                transports_.erase(t);
    
            // If UDP
            const auto type = tp->key.type;
            //if (std::strlen(tp->type_name) >= 3 && std::strncmp(tp->type_name, "UDP", 3ul) == 0) {
            if (type == PJSIP_TRANSPORT_UDP || type == PJSIP_TRANSPORT_UDP6) {
                SFL_WARN("UDP transport destroy");
    
                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();
                }
            } else if (type == ice_pj_transport_type_) {
                SFL_WARN("ICE transport destroy");
                std::unique_lock<std::mutex> lock(iceMutex_);
                const auto transport_key = std::find_if(iceTransports_.begin(), iceTransports_.end(), [tp](const SipIceTransport& i) {
                    return reinterpret_cast<const pjsip_transport*>(&i) == tp;
                });
                if (transport_key != iceTransports_.end())
                    iceTransports_.erase(transport_key);
            }
        }
    }
    
    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
    SipTransportBroker::waitForReleased(const SipTransportDescr& tp, std::function<void(bool)> released_cb)
    {
        std::vector<std::pair<SipTransportDescr, pjsip_transport*>> to_destroy_all;
        bool destroyed = false;
        {
            std::unique_lock<std::mutex> lock(transportMapMutex_);
            auto check_destroyed = [&](){
                return udpTransports_.find(tp) == udpTransports_.end();
            };
            destroyed = transportDestroyedCv_.wait_for(lock, std::chrono::seconds(10), check_destroyed);
            if (!destroyed)
                destroyed = check_destroyed();
        }
    
        if (released_cb)
            released_cb(destroyed);
    }
    
    std::shared_ptr<SipTransport>
    SipTransportBroker::getUdpTransport(const SipTransportDescr& descr)
    {
        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()) {
                    SFL_DBG("Reusing transport %s", descr.toString().c_str());
                    return spt;
                }
                else {
                    // Transport still exists but have not been destroyed yet.
                    SFL_WARN("Recycling transport %s", descr.toString().c_str());
                    auto ret = std::make_shared<SipTransport>(itp->second);
                    it->second = ret;
                    return ret;
                }
            } else {
                SFL_WARN("Cleaning up UDP transport %s", descr.toString().c_str());
                udpTransports_.erase(itp);
            }
        }
        auto ret = createUdpTransport(descr);
        if (ret) {
            udpTransports_[descr] = ret->get();
            transports_[ret->get()] = ret;
        }
        return ret;
    }
    
    std::shared_ptr<SipTransport>
    SipTransportBroker::createUdpTransport(const SipTransportDescr& d)
    {
        RETURN_IF_FAIL(d.listenerPort != 0, nullptr, "Could not determine port for this transport");
        auto family = pjsip_transport_type_get_af(d.type);
    
        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");
        pjsip_transport *transport = 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) {
            SFL_ERR("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;
        }
    
        SFL_DBG("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
    std::shared_ptr<TlsListener>
    SipTransportBroker::getTlsListener(const SipTransportDescr& d, const pjsip_tls_setting* settings)
    {
        RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
        auto family = pjsip_transport_type_get_af(d.type);
    
        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");
        SFL_DBG("Creating TLS listener %s on %s...", d.toString().c_str(), listeningAddress.toString(true).c_str());
    #if 0
        SFL_DBG(" ca_list_file : %s", settings->ca_list_file.ptr);
        SFL_DBG(" cert_file    : %s", settings->cert_file.ptr);
        SFL_DBG(" ciphers_num    : %d", settings->ciphers_num);
        SFL_DBG(" verify server %d client %d client_cert %d", settings->verify_server, settings->verify_client, settings->require_client_cert);
        SFL_DBG(" reuse_addr    : %d", settings->reuse_addr);
    #endif
    
        pjsip_tpfactory *listener = nullptr;
        const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, listeningAddress.pjPtr(), nullptr, 1, &listener);
        if (status != PJ_SUCCESS) {
            SFL_ERR("TLS listener did not start");
            sip_utils::sip_strerror(status);
            return nullptr;
        }
        return std::make_shared<TlsListener>(listener);
    }
    
    std::shared_ptr<SipTransport>
    SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l, const IpAddr& remote)
    {
        if (!l || !remote)
            return nullptr;
        IpAddr remoteAddr {remote};
        if (remoteAddr.getPort() == 0)
            remoteAddr.setPort(pjsip_transport_get_default_port_for_type(l->get()->type));
    
        SFL_DBG("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_,
                l->get()->type,
                remoteAddr.pjPtr(),
                remoteAddr.getLength(),
                &sel,
                &transport);
    
        if (!transport || status != PJ_SUCCESS) {
            SFL_ERR("Could not get new TLS transport");
            sip_utils::sip_strerror(status);
            return nullptr;
        }
        auto ret = std::make_shared<SipTransport>(transport, l);
        pjsip_transport_dec_ref(transport);
        {
            std::lock_guard<std::mutex> lock(transportMapMutex_);
            transports_[ret->get()] = ret;
        }
        return ret;
    }
    #endif
    
    std::shared_ptr<SipTransport>
    SipTransportBroker::getIceTransport(const std::shared_ptr<sfl::IceTransport>& ice)
    {
        std::unique_lock<std::mutex> lock(iceMutex_);
        iceTransports_.emplace_front(endpt_, pool_, ice_pj_transport_type_, ice, 0);
        auto& sip_ice_tr = iceTransports_.front();
        auto ret = std::make_shared<SipTransport>(&sip_ice_tr.base);
        {
            std::unique_lock<std::mutex> lock(transportMapMutex_);
            transports_[ret->get()] = ret;
        }
        sip_ice_tr.start();
        return ret;
    }
    
    std::vector<pj_sockaddr>
    SipTransportBroker::getSTUNAddresses(const pj_str_t serverName, pj_uint16_t port, std::vector<long> &socketDescriptors) const
    {
        const size_t ip_num = socketDescriptors.size();
        pj_sockaddr_in ipv4[ip_num];
        pj_status_t ret = pjstun_get_mapped_addr(&cp_.factory,
                socketDescriptors.size(), &socketDescriptors[0],
                &serverName, port, &serverName, port, ipv4);
    
        if (ret != PJ_SUCCESS) {
            SFL_ERR("STUN query to server \"%.*s\" failed", serverName.slen, serverName.ptr);
            switch (ret) {
                case PJLIB_UTIL_ESTUNNOTRESPOND:
                    SFL_ERR("No response from STUN server(s)");
                    break;
                case PJLIB_UTIL_ESTUNSYMMETRIC:
                    SFL_ERR("Different mapped addresses are returned by servers.");
                    break;
                default:
                    break;
            }
            throw std::runtime_error("Can't resolve STUN request");
        }
    
        std::vector<pj_sockaddr> result(ip_num);
        for(size_t i=0; i<ip_num; i++) {
            result[i].ipv4 = ipv4[i];
            SFL_WARN("STUN PORTS: %ld", pj_sockaddr_get_port(&result[i]));
        }
        return result;
    }
    
    #define RETURN_IF_NULL(A, M, ...) if ((A) == NULL) { SFL_ERR(M, ##__VA_ARGS__); return; }
    
    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);
    
        // Initialize the sip address with the hostname
        const pj_str_t *pjMachineName = pj_gethostname();
        addr = std::string(pjMachineName->ptr, pjMachineName->slen);
    
        // Update address and port with active transport
        RETURN_IF_NULL(transport, "Transport is NULL in findLocalAddress, using local address %s :%d", addr.c_str(), port);
    
        // get the transport manager associated with the SIP enpoint
        pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt_);
        RETURN_IF_NULL(tpmgr, "Transport manager is NULL in findLocalAddress, using local address %s :%d", addr.c_str(), port);
    
        pj_str_t pjstring;
        pj_cstr(&pjstring, host.c_str());
        pjsip_tpselector tp_sel = getTransportSelector(transport);
        pjsip_tpmgr_fla2_param param = {transportType, &tp_sel, pjstring, PJ_FALSE, {nullptr, 0}, 0, nullptr};
        if (pjsip_tpmgr_find_local_addr2(tpmgr, &pool_, &param) != PJ_SUCCESS) {
            SFL_WARN("Could not retrieve local address and port from transport, using %s :%d", addr.c_str(), port);
            return;
        }
    
        // Update local address based on the transport type
        addr = std::string(param.ret_addr.ptr, param.ret_addr.slen);
    
        // Determine the local port based on transport information
        port = param.ret_port;
    }
    
    void
    SipTransportBroker::findLocalAddressFromSTUN(pjsip_transport *transport,
                                           pj_str_t *stunServerName,
                                           int stunPort,
                                           std::string &addr, pj_uint16_t &port) const
    {
        // Initialize the sip port with the default SIP port
        port = DEFAULT_SIP_PORT;
    
        // Initialize the sip address with the hostname
        const pj_str_t *pjMachineName = pj_gethostname();
        addr = std::string(pjMachineName->ptr, pjMachineName->slen);
    
        // Update address and port with active transport
        RETURN_IF_NULL(transport, "Transport is NULL in findLocalAddress, using local address %s:%d", addr.c_str(), port);
    
        IpAddr mapped_addr;
        pj_sock_t sipSocket = pjsip_udp_transport_get_socket(transport);
        const pjstun_setting stunOpt = {PJ_TRUE, *stunServerName, stunPort, *stunServerName, stunPort};
        const pj_status_t stunStatus = pjstun_get_mapped_addr2(&cp_.factory, &stunOpt, 1, &sipSocket, &static_cast<pj_sockaddr_in&>(mapped_addr));
    
        switch (stunStatus) {
            case PJLIB_UTIL_ESTUNNOTRESPOND:
               SFL_ERR("No response from STUN server %.*s", stunServerName->slen, stunServerName->ptr);
               return;
            case PJLIB_UTIL_ESTUNSYMMETRIC:
               SFL_ERR("Different mapped addresses are returned by servers.");
               return;
            case PJ_SUCCESS:
                port = mapped_addr.getPort();
                addr = mapped_addr.toString();
            default:
               break;
        }
    
        SFL_WARN("Using address %s provided by STUN server %.*s",
             IpAddr(mapped_addr).toString(true).c_str(), stunServerName->slen, stunServerName->ptr);
    }
    
    #undef RETURN_IF_NULL