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
|