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