LCOV - code coverage report
Current view: top level - src/connectivity - sip_utils.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 56.2 % 130 73
Test Date: 2026-06-13 09:18:46 Functions: 50.0 % 26 13

            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_LOG("Adding route {}", host);
      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          101 : parseDisplayName(const pjsip_name_addr* sip_name_addr)
     105              : {
     106          101 :     if (not sip_name_addr->display.ptr or not sip_name_addr->display.slen)
     107            0 :         return {};
     108              : 
     109          101 :     auto displayName = as_view(sip_name_addr->display);
     110              : 
     111              :     // Filter out invalid UTF-8 characters to avoid getting kicked from D-Bus
     112          101 :     if (not utf8_validate(displayName))
     113            0 :         return utf8_make_valid(displayName);
     114              : 
     115          202 :     return std::string(displayName);
     116              : }
     117              : 
     118              : std::string
     119          101 : parseDisplayName(const pjsip_from_hdr* header)
     120              : {
     121              :     // PJSIP return a pjsip_name_addr for To, From and Contact headers
     122          101 :     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          312 : addContactHeader(const std::string& contactHdr, pjsip_tx_data* tdata)
     174              : {
     175          312 :     if (contactHdr.empty()) {
     176            0 :         JAMI_WARNING("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          312 :     auto pjContact = pj_strdup3(tdata->pool, contactHdr.c_str());
     187              : 
     188          312 :     pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool);
     189          312 :     contact->uri = pjsip_parse_uri(tdata->pool, pjContact.ptr, pjContact.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
     190              :     // remove old contact header (if present)
     191          312 :     pjsip_msg_find_remove_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
     192          312 :     pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact);
     193              : }
     194              : 
     195              : void
     196          437 : addUserAgentHeader(const std::string& userAgent, pjsip_tx_data* tdata)
     197              : {
     198          437 :     if (tdata == nullptr or userAgent.empty())
     199            0 :         return;
     200              : 
     201          437 :     auto pjUserAgent = CONST_PJ_STR(userAgent);
     202              : 
     203              :     // Do nothing if user-agent header is present.
     204          437 :     if (pjsip_msg_find_hdr_by_name(tdata->msg, &USER_AGENT_STR, nullptr) != nullptr) {
     205           95 :         return;
     206              :     }
     207              : 
     208              :     // Add Header
     209          342 :     auto* hdr = reinterpret_cast<pjsip_hdr*>(pjsip_user_agent_hdr_create(tdata->pool, &USER_AGENT_STR, &pjUserAgent));
     210              : 
     211          342 :     if (hdr != nullptr) {
     212          342 :         pjsip_msg_add_hdr(tdata->msg, hdr);
     213              :     }
     214              : }
     215              : 
     216              : std::string_view
     217          475 : getPeerUserAgent(const pjsip_rx_data* rdata)
     218              : {
     219          475 :     if (rdata == nullptr or rdata->msg_info.msg == nullptr) {
     220            0 :         JAMI_ERROR("Unexpected null pointer!");
     221            0 :         return {};
     222              :     }
     223              : 
     224          475 :     if (auto* uaHdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
     225              :                                                                              &USER_AGENT_STR,
     226              :                                                                              nullptr)) {
     227          385 :         return as_view(uaHdr->hvalue);
     228              :     }
     229           90 :     return {};
     230              : }
     231              : 
     232              : std::vector<std::string>
     233          475 : getPeerAllowMethods(const pjsip_rx_data* rdata)
     234              : {
     235          475 :     if (rdata == nullptr or rdata->msg_info.msg == nullptr) {
     236            0 :         JAMI_ERROR("Unexpected null pointer!");
     237            0 :         return {};
     238              :     }
     239              : 
     240          475 :     std::vector<std::string> methods;
     241              : 
     242              :     pjsip_allow_hdr* allow = static_cast<pjsip_allow_hdr*>(
     243          475 :         pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ALLOW, nullptr));
     244              : 
     245          475 :     if (allow != nullptr) {
     246          296 :         methods.reserve(allow->count);
     247         3847 :         for (unsigned i = 0; i < allow->count; i++) {
     248         3551 :             methods.emplace_back(allow->values[i].ptr, allow->values[i].slen);
     249              :         }
     250              :     }
     251              : 
     252          475 :     return methods;
     253          475 : }
     254              : 
     255              : void
     256          101 : logMessageHeaders(const pjsip_hdr* hdr_list)
     257              : {
     258          101 :     const pjsip_hdr* hdr = hdr_list->next;
     259          101 :     const pjsip_hdr* end = hdr_list;
     260          101 :     std::string msgHdrStr("Message headers:\n");
     261         1414 :     for (; hdr != end; hdr = hdr->next) {
     262              :         char buf[1024];
     263         1313 :         int size = pjsip_hdr_print_on((void*) hdr, buf, sizeof(buf));
     264         1313 :         if (size > 0) {
     265         1313 :             msgHdrStr.append(buf, size);
     266         1313 :             msgHdrStr.push_back('\n');
     267              :         }
     268              :     }
     269              : 
     270          404 :     JAMI_LOG("{}", msgHdrStr);
     271          101 : }
     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            3 :     return std::string {ret.ptr, ret.ptr + ret.slen};
     279              : }
     280              : 
     281              : std::string
     282          737 : streamId(const std::string& callId, std::string_view label)
     283              : {
     284          737 :     if (callId.empty())
     285           98 :         return fmt::format("host_{}", label);
     286         1376 :     return fmt::format("{}_{}", callId, label);
     287              : }
     288              : 
     289              : void
     290          348 : sockaddr_to_host_port(pj_pool_t* pool, pjsip_host_port* host_port, const pj_sockaddr* addr)
     291              : {
     292          348 :     host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN + 4);
     293          348 :     pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN + 4, 0);
     294          348 :     host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
     295          348 :     host_port->port = pj_sockaddr_get_port(addr);
     296          348 : }
     297              : 
     298              : } // namespace sip_utils
     299              : } // namespace jami
        

Generated by: LCOV version 2.0-1