LCOV - code coverage report
Current view: top level - foo/src/connectivity - sip_utils.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 74 130 56.9 %
Date: 2026-02-28 10:41:24 Functions: 13 18 72.2 %

          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 "connectivity/sip_utils.h"
      19             : #include "logger.h"
      20             : #include "connectivity/utf8_utils.h"
      21             : 
      22             : #include <pjsip.h>
      23             : #include <pjsip_ua.h>
      24             : #include <pjlib-util.h>
      25             : #include <pjnath.h>
      26             : #include <pjnath/stun_config.h>
      27             : #include <pj/string.h>
      28             : #include <pjsip/sip_types.h>
      29             : #include <pjsip/sip_uri.h>
      30             : #include <pj/list.h>
      31             : 
      32             : #ifndef _WIN32
      33             : #include <netdb.h>
      34             : #include <sys/socket.h>
      35             : #include <netinet/in.h>
      36             : #include <arpa/inet.h>
      37             : #endif
      38             : 
      39             : #include <vector>
      40             : 
      41             : using namespace std::literals;
      42             : 
      43             : namespace jami {
      44             : namespace sip_utils {
      45             : 
      46             : constexpr pj_str_t USER_AGENT_STR = CONST_PJ_STR("User-Agent");
      47             : 
      48             : std::string
      49           0 : PjsipErrorCategory::message(int condition) const
      50             : {
      51           0 :     std::string err_msg;
      52           0 :     err_msg.reserve(PJ_ERR_MSG_SIZE);
      53           0 :     err_msg.resize(pj_strerror(condition, &err_msg[0], err_msg.capacity()).slen);
      54           0 :     return err_msg;
      55           0 : }
      56             : 
      57             : std::string
      58           0 : fetchHeaderValue(pjsip_msg* msg, const std::string& field)
      59             : {
      60           0 :     pj_str_t name = pj_str((char*) field.c_str());
      61           0 :     pjsip_generic_string_hdr* hdr = static_cast<pjsip_generic_string_hdr*>(pjsip_msg_find_hdr_by_name(msg, &name, NULL));
      62             : 
      63           0 :     if (!hdr)
      64           0 :         return "";
      65             : 
      66           0 :     std::string value(hdr->hvalue.ptr, hdr->hvalue.slen);
      67             : 
      68           0 :     size_t pos = value.find('\n');
      69             : 
      70           0 :     if (pos != std::string::npos)
      71           0 :         return value.substr(0, pos);
      72             :     else
      73           0 :         return "";
      74           0 : }
      75             : 
      76             : pjsip_route_hdr*
      77           0 : createRouteSet(const std::string& route, pj_pool_t* hdr_pool)
      78             : {
      79           0 :     pjsip_route_hdr* route_set = pjsip_route_hdr_create(hdr_pool);
      80             : 
      81           0 :     std::string host;
      82           0 :     int port = 0;
      83           0 :     size_t found = route.find(':');
      84           0 :     if (found != std::string::npos) {
      85           0 :         host = route.substr(0, found);
      86           0 :         port = atoi(route.substr(found + 1, route.length() - found).c_str());
      87             :     } else
      88           0 :         host = route;
      89             : 
      90           0 :     pjsip_route_hdr* routing = pjsip_route_hdr_create(hdr_pool);
      91           0 :     pjsip_sip_uri* url = pjsip_sip_uri_create(hdr_pool, 0);
      92           0 :     url->lr_param = 1;
      93           0 :     routing->name_addr.uri = (pjsip_uri*) url;
      94           0 :     pj_strdup2(hdr_pool, &url->host, host.c_str());
      95           0 :     url->port = port;
      96             : 
      97           0 :     JAMI_DBG("Adding route %s", host.c_str());
      98           0 :     pj_list_push_back(route_set, pjsip_hdr_clone(hdr_pool, routing));
      99             : 
     100           0 :     return route_set;
     101           0 : }
     102             : 
     103             : std::string
     104          95 : parseDisplayName(const pjsip_name_addr* sip_name_addr)
     105             : {
     106          95 :     if (not sip_name_addr->display.ptr or not sip_name_addr->display.slen)
     107           0 :         return {};
     108             : 
     109          95 :     auto displayName = as_view(sip_name_addr->display);
     110             : 
     111             :     // Filter out invalid UTF-8 characters to avoid getting kicked from D-Bus
     112          95 :     if (not utf8_validate(displayName))
     113           0 :         return utf8_make_valid(displayName);
     114             : 
     115          95 :     return std::string(displayName);
     116             : }
     117             : 
     118             : std::string
     119          95 : parseDisplayName(const pjsip_from_hdr* header)
     120             : {
     121             :     // PJSIP return a pjsip_name_addr for To, From and Contact headers
     122          95 :     return parseDisplayName(reinterpret_cast<pjsip_name_addr*>(header->uri));
     123             : }
     124             : 
     125             : std::string
     126           0 : parseDisplayName(const pjsip_contact_hdr* header)
     127             : {
     128             :     // PJSIP return a pjsip_name_addr for To, From and Contact headers
     129           0 :     return parseDisplayName(reinterpret_cast<pjsip_name_addr*>(header->uri));
     130             : }
     131             : 
     132             : std::string_view
     133           1 : stripSipUriPrefix(std::string_view sipUri)
     134             : {
     135             :     // Remove sip: prefix
     136             :     static constexpr auto SIP_PREFIX = "sip:"sv;
     137           1 :     size_t found = sipUri.find(SIP_PREFIX);
     138             : 
     139           1 :     if (found != std::string_view::npos)
     140           1 :         sipUri = sipUri.substr(found + SIP_PREFIX.size());
     141             : 
     142             :     // URI may or may not be between brackets
     143           1 :     found = sipUri.find('<');
     144           1 :     if (found != std::string_view::npos)
     145           0 :         sipUri = sipUri.substr(found + 1);
     146             : 
     147           1 :     found = sipUri.find('@');
     148           1 :     if (found != std::string_view::npos)
     149           1 :         sipUri = sipUri.substr(0, found);
     150             : 
     151           1 :     found = sipUri.find('>');
     152           1 :     if (found != std::string_view::npos)
     153           0 :         sipUri = sipUri.substr(0, found);
     154             : 
     155           1 :     return sipUri;
     156             : }
     157             : 
     158             : std::string_view
     159           0 : getHostFromUri(std::string_view uri)
     160             : {
     161           0 :     auto found = uri.find('@');
     162           0 :     if (found != std::string_view::npos)
     163           0 :         uri = uri.substr(found + 1);
     164             : 
     165           0 :     found = uri.find('>');
     166           0 :     if (found != std::string_view::npos)
     167           0 :         uri = uri.substr(0, found);
     168             : 
     169           0 :     return uri;
     170             : }
     171             : 
     172             : void
     173         291 : addContactHeader(const std::string& contactHdr, pjsip_tx_data* tdata)
     174             : {
     175         291 :     if (contactHdr.empty()) {
     176           0 :         JAMI_WARN("Contact header is unable to be added (empty string)");
     177           0 :         return;
     178             :     }
     179             : 
     180             :     /*
     181             :      * Duplicate contact header because tdata->msg keep a reference to it and
     182             :      * can be used in a callback after destruction of the contact header in
     183             :      * Jami.  Bind lifetime of the duplicated string to the pool allocator of
     184             :      * tdata.
     185             :      */
     186         291 :     auto pjContact = pj_strdup3(tdata->pool, contactHdr.c_str());
     187             : 
     188         291 :     pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool);
     189         291 :     contact->uri = pjsip_parse_uri(tdata->pool, pjContact.ptr, pjContact.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
     190             :     // remove old contact header (if present)
     191         291 :     pjsip_msg_find_remove_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
     192         291 :     pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact);
     193             : }
     194             : 
     195             : void
     196         407 : addUserAgentHeader(const std::string& userAgent, pjsip_tx_data* tdata)
     197             : {
     198         407 :     if (tdata == nullptr or userAgent.empty())
     199          89 :         return;
     200             : 
     201         407 :     auto pjUserAgent = CONST_PJ_STR(userAgent);
     202             : 
     203             :     // Do nothing if user-agent header is present.
     204         407 :     if (pjsip_msg_find_hdr_by_name(tdata->msg, &USER_AGENT_STR, nullptr) != nullptr) {
     205          89 :         return;
     206             :     }
     207             : 
     208             :     // Add Header
     209         318 :     auto* hdr = reinterpret_cast<pjsip_hdr*>(pjsip_user_agent_hdr_create(tdata->pool, &USER_AGENT_STR, &pjUserAgent));
     210             : 
     211         318 :     if (hdr != nullptr) {
     212         318 :         pjsip_msg_add_hdr(tdata->msg, hdr);
     213             :     }
     214             : }
     215             : 
     216             : std::string_view
     217         446 : getPeerUserAgent(const pjsip_rx_data* rdata)
     218             : {
     219         446 :     if (rdata == nullptr or rdata->msg_info.msg == nullptr) {
     220           0 :         JAMI_ERR("Unexpected null pointer!");
     221           0 :         return {};
     222             :     }
     223             : 
     224         446 :     if (auto* uaHdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
     225             :                                                                              &USER_AGENT_STR,
     226             :                                                                              nullptr)) {
     227         361 :         return as_view(uaHdr->hvalue);
     228             :     }
     229          85 :     return {};
     230             : }
     231             : 
     232             : std::vector<std::string>
     233         446 : getPeerAllowMethods(const pjsip_rx_data* rdata)
     234             : {
     235         446 :     if (rdata == nullptr or rdata->msg_info.msg == nullptr) {
     236           0 :         JAMI_ERR("Unexpected null pointer!");
     237           0 :         return {};
     238             :     }
     239             : 
     240         446 :     std::vector<std::string> methods;
     241             : 
     242             :     pjsip_allow_hdr* allow = static_cast<pjsip_allow_hdr*>(
     243         446 :         pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ALLOW, nullptr));
     244             : 
     245         446 :     if (allow != nullptr) {
     246         278 :         methods.reserve(allow->count);
     247        3613 :         for (unsigned i = 0; i < allow->count; i++) {
     248        3335 :             methods.emplace_back(allow->values[i].ptr, allow->values[i].slen);
     249             :         }
     250             :     }
     251             : 
     252         446 :     return methods;
     253         446 : }
     254             : 
     255             : void
     256          95 : logMessageHeaders(const pjsip_hdr* hdr_list)
     257             : {
     258          95 :     const pjsip_hdr* hdr = hdr_list->next;
     259          95 :     const pjsip_hdr* end = hdr_list;
     260          95 :     std::string msgHdrStr("Message headers:\n");
     261        1330 :     for (; hdr != end; hdr = hdr->next) {
     262             :         char buf[1024];
     263        1235 :         int size = pjsip_hdr_print_on((void*) hdr, buf, sizeof(buf));
     264        1235 :         if (size > 0) {
     265        1235 :             msgHdrStr.append(buf, size);
     266        1235 :             msgHdrStr.push_back('\n');
     267             :         }
     268             :     }
     269             : 
     270         380 :     JAMI_LOG("{}", msgHdrStr);
     271          95 : }
     272             : 
     273             : std::string
     274           1 : sip_strerror(pj_status_t code)
     275             : {
     276             :     char err_msg[PJ_ERR_MSG_SIZE];
     277           1 :     auto ret = pj_strerror(code, err_msg, sizeof err_msg);
     278           1 :     return std::string {ret.ptr, ret.ptr + ret.slen};
     279             : }
     280             : 
     281             : std::string
     282         719 : streamId(const std::string& callId, std::string_view label)
     283             : {
     284         719 :     if (callId.empty())
     285          98 :         return fmt::format("host_{}", label);
     286        1340 :     return fmt::format("{}_{}", callId, label);
     287             : }
     288             : 
     289             : void
     290         352 : sockaddr_to_host_port(pj_pool_t* pool, pjsip_host_port* host_port, const pj_sockaddr* addr)
     291             : {
     292         352 :     host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN + 4);
     293         352 :     pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN + 4, 0);
     294         352 :     host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
     295         352 :     host_port->port = pj_sockaddr_get_port(addr);
     296         352 : }
     297             : 
     298             : } // namespace sip_utils
     299             : } // namespace jami

Generated by: LCOV version 1.14