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
|