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: 71 130 54.6 %
Date: 2025-12-18 10:07:43 Functions: 12 18 66.7 %

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

Generated by: LCOV version 1.14