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