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 "sip/sipvoiplink.h"
19 :
20 : #ifdef HAVE_CONFIG_H
21 : #include "config.h"
22 : #endif
23 :
24 : #include "sdp.h"
25 : #include "sip/sipcall.h"
26 : #include "sip/sipaccount.h"
27 :
28 : #include "jamidht/jamiaccount.h"
29 :
30 : #include "manager.h"
31 :
32 : #include "im/instant_messaging.h"
33 :
34 : #include "pres_sub_server.h"
35 :
36 : #include "connectivity/sip_utils.h"
37 : #include "string_utils.h"
38 : #include "logger.h"
39 :
40 : #include <dhtnet/ip_utils.h>
41 : #include <opendht/thread_pool.h>
42 :
43 : #include <pjsip/sip_endpoint.h>
44 : #include <pjsip/sip_uri.h>
45 :
46 : #include <pjsip-simple/presence.h>
47 : #include <pjsip-simple/publish.h>
48 :
49 : // Only PJSIP 2.10 is supported.
50 : #if PJ_VERSION_NUM < (2 << 24 | 10 << 16)
51 : #error "Unsupported PJSIP version (requires version 2.10+)"
52 : #endif
53 :
54 : #include <cstddef>
55 : #include <regex>
56 :
57 : // On Windows, gai_strerror is a macro that expands to a wide-string
58 : // version; use the narrow-string variant explicitly.
59 : #ifdef _WIN32
60 : #define jami_gai_strerror gai_strerrorA
61 : #else
62 : #define jami_gai_strerror gai_strerror
63 : #endif
64 :
65 : namespace jami {
66 :
67 : using sip_utils::CONST_PJ_STR;
68 :
69 : /**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/
70 :
71 : static pjsip_endpoint* endpt_;
72 : static pjsip_module mod_ua_;
73 : static pjsip_module mod_handover_admission_;
74 : static pj_bool_t handover_request_cb(pjsip_rx_data* rdata);
75 :
76 : static void invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* e);
77 : static void outgoing_request_forked_cb(pjsip_inv_session* inv, pjsip_event* e);
78 : static void transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjsip_event* e);
79 : // Called when an SDP offer is found in answer. This will occur
80 : // when we send an empty invite (no SDP). In this case, we should
81 : // expect an offer in a the 200 OK message
82 : static void on_rx_offer2(pjsip_inv_session* inv, struct pjsip_inv_on_rx_offer_cb_param* param);
83 : // Called when a re-invite is received
84 : static pj_status_t reinvite_received_cb(pjsip_inv_session* inv, const pjmedia_sdp_session* offer, pjsip_rx_data* rdata);
85 : // Called to request an SDP offer if the peer sent an invite or
86 : // a re-invite with no SDP. In this, we must provide an offer in
87 : // the answer (200 OK) if we accept the call
88 : static void sdp_create_offer_cb(pjsip_inv_session* inv, pjmedia_sdp_session** p_offer);
89 : // Called to report media (SDP) negotiation result
90 : static void sdp_media_update_cb(pjsip_inv_session* inv, pj_status_t status);
91 :
92 : static std::shared_ptr<SIPCall> getCallFromInvite(pjsip_inv_session* inv);
93 : #ifdef DEBUG_SIP_REQUEST_MSG
94 : static void processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* e);
95 : #endif
96 :
97 : static pj_bool_t
98 5 : handleIncomingOptions(pjsip_rx_data* rdata)
99 : {
100 : pjsip_tx_data* tdata;
101 :
102 5 : auto* dlg = pjsip_rdata_get_dlg(rdata);
103 5 : if (dlg) {
104 0 : JAMI_LOG("Processing in-dialog option request");
105 0 : if (pjsip_dlg_create_response(dlg, rdata, PJSIP_SC_OK, NULL, &tdata) != PJ_SUCCESS) {
106 0 : JAMI_ERROR("Failed to create in-dialog response for option request");
107 0 : return PJ_FALSE;
108 : }
109 : } else {
110 5 : JAMI_LOG("Processing out-of-dialog option request");
111 5 : if (pjsip_endpt_create_response(endpt_, rdata, PJSIP_SC_OK, NULL, &tdata) != PJ_SUCCESS) {
112 0 : JAMI_ERROR("Failed to create out-of-dialog response for option request");
113 0 : return PJ_FALSE;
114 : }
115 : }
116 :
117 : #define ADD_HDR(hdr) \
118 : do { \
119 : const pjsip_hdr* cap_hdr = hdr; \
120 : if (cap_hdr) \
121 : pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); \
122 : } while (0)
123 : #define ADD_CAP(cap) ADD_HDR(pjsip_endpt_get_capability(endpt_, cap, NULL));
124 :
125 5 : ADD_CAP(PJSIP_H_ALLOW);
126 5 : ADD_CAP(PJSIP_H_ACCEPT);
127 5 : ADD_CAP(PJSIP_H_SUPPORTED);
128 5 : ADD_HDR(pjsip_evsub_get_allow_events_hdr(NULL));
129 :
130 5 : if (dlg) {
131 0 : if (pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata) != PJ_SUCCESS) {
132 0 : JAMI_ERROR("Failed to send in-dialog response for option request");
133 0 : return PJ_FALSE;
134 : }
135 :
136 0 : JAMI_LOG("Sent in-dialog response for option request");
137 0 : return PJ_TRUE;
138 : }
139 :
140 : pjsip_response_addr res_addr;
141 5 : pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
142 :
143 5 : if (pjsip_endpt_send_response(endpt_, &res_addr, tdata, NULL, NULL) != PJ_SUCCESS) {
144 0 : pjsip_tx_data_dec_ref(tdata);
145 0 : JAMI_ERROR("Failed to send out-of-dialog response for option request");
146 0 : return PJ_FALSE;
147 : }
148 :
149 5 : JAMI_LOG("Sent out-of-dialog response for option request");
150 5 : return PJ_TRUE;
151 : }
152 :
153 : // return PJ_FALSE so that eventually other modules will handle these requests
154 : // TODO: move Voicemail to separate module
155 : static pj_bool_t
156 0 : transaction_response_cb(pjsip_rx_data* rdata)
157 : {
158 0 : pjsip_dialog* dlg = pjsip_rdata_get_dlg(rdata);
159 :
160 0 : if (!dlg)
161 0 : return PJ_FALSE;
162 :
163 0 : pjsip_transaction* tsx = pjsip_rdata_get_tsx(rdata);
164 :
165 0 : if (!tsx or tsx->method.id != PJSIP_INVITE_METHOD)
166 0 : return PJ_FALSE;
167 :
168 0 : if (tsx->status_code / 100 == 2) {
169 : /**
170 : * Send an ACK message inside a transaction. PJSIP send automatically, non-2xx ACK response.
171 : * ACK for a 2xx response must be send using this method.
172 : */
173 : pjsip_tx_data* tdata;
174 :
175 0 : if (rdata->msg_info.cseq) {
176 0 : pjsip_dlg_create_request(dlg, &pjsip_ack_method, rdata->msg_info.cseq->cseq, &tdata);
177 0 : pjsip_dlg_send_request(dlg, tdata, -1, NULL);
178 : }
179 : }
180 :
181 0 : return PJ_FALSE;
182 : }
183 :
184 : static pj_status_t
185 0 : try_respond_stateless(pjsip_endpoint* endpt,
186 : pjsip_rx_data* rdata,
187 : int st_code,
188 : const pj_str_t* st_text,
189 : const pjsip_hdr* hdr_list,
190 : const pjsip_msg_body* body)
191 : {
192 : /* Check that no UAS transaction has been created for this request.
193 : * If UAS transaction has been created for this request, application
194 : * MUST send the response statefully using that transaction.
195 : */
196 0 : if (!pjsip_rdata_get_tsx(rdata))
197 0 : return pjsip_endpt_respond_stateless(endpt, rdata, st_code, st_text, hdr_list, body);
198 : else
199 0 : JAMI_ERROR("Transaction has been created for this request, send response statefully instead");
200 :
201 0 : return !PJ_SUCCESS;
202 : }
203 :
204 : template<typename T>
205 : static bool
206 118 : is_uninitialized(std::weak_ptr<T> const& weak)
207 : {
208 : using wt = std::weak_ptr<T>;
209 118 : return !weak.owner_before(wt {}) && !wt {}.owner_before(weak);
210 : }
211 :
212 : static pj_bool_t
213 118 : transaction_request_cb(pjsip_rx_data* rdata)
214 : {
215 118 : if (!rdata or !rdata->msg_info.msg) {
216 0 : JAMI_ERROR("rx_data is NULL");
217 0 : return PJ_FALSE;
218 : }
219 :
220 118 : pjsip_method* method = &rdata->msg_info.msg->line.req.method;
221 :
222 118 : if (!method) {
223 0 : JAMI_ERROR("method is NULL");
224 0 : return PJ_FALSE;
225 : }
226 :
227 118 : if (method->id == PJSIP_ACK_METHOD && pjsip_rdata_get_dlg(rdata))
228 0 : return PJ_FALSE;
229 :
230 118 : if (!rdata->msg_info.to or !rdata->msg_info.from or !rdata->msg_info.via) {
231 0 : JAMI_ERROR("Missing From, To or Via fields");
232 0 : return PJ_FALSE;
233 : }
234 :
235 118 : auto* const sip_to_uri = reinterpret_cast<pjsip_sip_uri*>(pjsip_uri_get_uri(rdata->msg_info.to->uri));
236 118 : auto* const sip_from_uri = reinterpret_cast<pjsip_sip_uri*>(pjsip_uri_get_uri(rdata->msg_info.from->uri));
237 118 : const pjsip_host_port& sip_via = rdata->msg_info.via->sent_by;
238 :
239 118 : if (!sip_to_uri or !sip_from_uri or !sip_via.host.ptr) {
240 0 : JAMI_ERROR("NULL URI");
241 0 : return PJ_FALSE;
242 : }
243 :
244 118 : std::string_view toUsername(sip_to_uri->user.ptr, sip_to_uri->user.slen);
245 118 : std::string_view toHost(sip_to_uri->host.ptr, sip_to_uri->host.slen);
246 118 : std::string_view viaHostname(sip_via.host.ptr, sip_via.host.slen);
247 118 : const std::string_view remote_user(sip_from_uri->user.ptr, sip_from_uri->user.slen);
248 118 : const std::string_view remote_hostname(sip_from_uri->host.ptr, sip_from_uri->host.slen);
249 118 : std::string peerNumber;
250 118 : if (not remote_user.empty() and not remote_hostname.empty())
251 236 : peerNumber = remote_user + "@" + remote_hostname;
252 : else {
253 : char tmp[PJSIP_MAX_URL_SIZE];
254 0 : size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
255 0 : peerNumber = sip_utils::stripSipUriPrefix(std::string_view(tmp, length));
256 : }
257 :
258 118 : auto transport = Manager::instance().sipVoIPLink().sipTransportBroker->addTransport(rdata->tp_info.transport);
259 :
260 118 : std::shared_ptr<SIPAccountBase> account;
261 : // If transport account is default-constructed, guessing account is allowed
262 118 : const auto& waccount = transport ? transport->getAccount() : std::weak_ptr<SIPAccountBase> {};
263 118 : if (is_uninitialized(waccount)) {
264 15 : account = Manager::instance().sipVoIPLink().guessAccount(toUsername, viaHostname, remote_hostname);
265 15 : if (not account)
266 0 : return PJ_FALSE;
267 15 : if (not transport and account->getAccountType() == SIPAccount::ACCOUNT_TYPE) {
268 0 : if (not(transport = std::static_pointer_cast<SIPAccount>(account)->getTransport())) {
269 0 : JAMI_ERROR("No suitable transport to answer this call.");
270 0 : return PJ_FALSE;
271 : }
272 0 : JAMI_WARNING("Using transport from account.");
273 : }
274 103 : } else if (!(account = waccount.lock())) {
275 0 : JAMI_ERROR("Dropping SIP request: account is expired.");
276 0 : return PJ_FALSE;
277 : }
278 :
279 118 : pjsip_msg_body* body = rdata->msg_info.msg->body;
280 :
281 118 : if (method->id == PJSIP_OTHER_METHOD) {
282 0 : std::string_view request = sip_utils::as_view(method->name);
283 :
284 0 : if (request.find(sip_utils::SIP_METHODS::NOTIFY) != std::string_view::npos) {
285 0 : if (body and body->data) {
286 0 : std::string_view body_view(static_cast<char*>(body->data), body->len);
287 0 : auto pos = body_view.find("Voice-Message: ");
288 0 : if (pos != std::string_view::npos) {
289 0 : int newCount {0};
290 0 : int oldCount {0};
291 0 : int urgentCount {0};
292 0 : std::string sp(body_view.substr(pos));
293 0 : int ret = sscanf(sp.c_str(), "Voice-Message: %d/%d (%d/", &newCount, &oldCount, &urgentCount);
294 :
295 : // According to rfc3842
296 : // urgent messages are optional
297 0 : if (ret >= 2)
298 0 : emitSignal<libjami::CallSignal::VoiceMailNotify>(account->getAccountID(),
299 : newCount,
300 : oldCount,
301 : urgentCount);
302 0 : }
303 : }
304 0 : } else if (request.find(sip_utils::SIP_METHODS::MESSAGE) != std::string_view::npos) {
305 : // Reply 200 immediately (RFC 3428, ch. 7)
306 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_OK, nullptr, nullptr, nullptr);
307 : // Process message content in case of multi-part body
308 0 : auto payloads = im::parseSipMessage(rdata->msg_info.msg);
309 0 : if (payloads.size() > 0) {
310 0 : constexpr pj_str_t STR_MESSAGE_ID = jami::sip_utils::CONST_PJ_STR("Message-ID");
311 0 : auto* msgId = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
312 : &STR_MESSAGE_ID,
313 : nullptr);
314 0 : std::string id = {};
315 0 : if (!msgId) {
316 : // Supports IMDN message format https://tools.ietf.org/html/rfc5438#section-7.1.1.3
317 0 : constexpr pj_str_t STR_IMDN_MESSAGE_ID = jami::sip_utils::CONST_PJ_STR("imdn.Message-ID");
318 0 : msgId = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
319 : &STR_IMDN_MESSAGE_ID,
320 : nullptr);
321 : }
322 0 : if (msgId)
323 0 : id = std::string(msgId->hvalue.ptr, msgId->hvalue.slen);
324 :
325 0 : if (not id.empty()) {
326 : try {
327 : // Mark message as treated
328 0 : auto intid = from_hex_string(id);
329 0 : auto acc = std::dynamic_pointer_cast<JamiAccount>(account);
330 0 : if (acc and acc->isMessageTreated(intid))
331 0 : return PJ_FALSE;
332 0 : } catch (const std::exception& e) {
333 0 : JAMI_WARNING("[Account {}] Couldn't treat message {}: {}",
334 : account->getAccountID(),
335 : from_hex_string(id),
336 : e.what());
337 0 : }
338 : }
339 0 : account->onTextMessage(id, peerNumber, transport->getTlsInfos().peerCert, payloads);
340 0 : }
341 0 : return PJ_FALSE;
342 0 : }
343 :
344 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_OK, NULL, NULL, NULL);
345 :
346 0 : return PJ_FALSE;
347 118 : } else if (method->id == PJSIP_OPTIONS_METHOD) {
348 5 : return handleIncomingOptions(rdata);
349 113 : } else if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
350 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL);
351 0 : return PJ_FALSE;
352 : }
353 :
354 113 : if (method->id == PJSIP_INVITE_METHOD) {
355 : // Log headers of received INVITE
356 113 : JAMI_LOG("Received a SIP INVITE request");
357 113 : sip_utils::logMessageHeaders(&rdata->msg_info.msg->hdr);
358 : }
359 :
360 113 : pjmedia_sdp_session* r_sdp {nullptr};
361 113 : if (body) {
362 113 : if (pjmedia_sdp_parse(rdata->tp_info.pool, (char*) body->data, body->len, &r_sdp) != PJ_SUCCESS) {
363 0 : JAMI_WARNING("Failed to parse the SDP in offer");
364 0 : r_sdp = nullptr;
365 : }
366 : }
367 :
368 113 : if (not account->hasActiveCodec(MEDIA_AUDIO)) {
369 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, NULL);
370 0 : return PJ_FALSE;
371 : }
372 :
373 : // Verify that we can handle the request
374 113 : unsigned options = 0;
375 :
376 113 : if (pjsip_inv_verify_request(rdata, &options, NULL, NULL, endpt_, NULL) != PJ_SUCCESS) {
377 0 : JAMI_ERROR("Unable to verify INVITE request in secure dialog.");
378 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL);
379 0 : return PJ_FALSE;
380 : }
381 :
382 : // Build the initial media using the remote offer.
383 113 : auto localMediaList = Sdp::getMediaAttributeListFromSdp(r_sdp);
384 :
385 : // To enable video, it must be enabled in the remote and locally (i.e. in the account)
386 315 : for (auto& media : localMediaList) {
387 202 : if (media.type_ == MediaType::MEDIA_VIDEO) {
388 88 : media.enabled_ &= account->isVideoEnabled();
389 : }
390 : }
391 :
392 339 : auto call = account->newIncomingCall(std::string(remote_user),
393 113 : MediaAttribute::mediaAttributesToMediaMaps(localMediaList),
394 226 : transport);
395 113 : if (!call) {
396 0 : return PJ_FALSE;
397 : }
398 :
399 113 : call->setPeerUaVersion(sip_utils::getPeerUserAgent(rdata));
400 113 : if (std::dynamic_pointer_cast<JamiAccount>(account))
401 103 : call->setPeerHandoverSupported(sip_utils::supportsCallHandover(rdata));
402 : // The username can be used to join specific calls in conversations
403 226 : call->toUsername(std::string(toUsername));
404 :
405 : // FIXME: for now, use the same address family as the SIP transport
406 113 : auto family = pjsip_transport_type_get_af(pjsip_transport_get_type_from_flag(transport->get()->flag));
407 :
408 113 : dhtnet::IpAddr addrSdp;
409 113 : if (account->getUPnPActive()) {
410 : /* use UPnP addr, or published addr if its set */
411 0 : addrSdp = account->getPublishedSameasLocal() ? account->getUPnPIpAddress() : account->getPublishedIpAddress();
412 : } else {
413 226 : addrSdp = account->isStunEnabled() or (not account->getPublishedSameasLocal())
414 226 : ? account->getPublishedIpAddress()
415 113 : : dhtnet::ip_utils::getInterfaceAddr(account->getLocalInterface(), family);
416 : }
417 :
418 : /* fallback on local address */
419 113 : if (not addrSdp)
420 0 : addrSdp = dhtnet::ip_utils::getInterfaceAddr(account->getLocalInterface(), family);
421 :
422 : // Try to obtain display name from From: header first, fallback on Contact:
423 113 : auto peerDisplayName = sip_utils::parseDisplayName(rdata->msg_info.from);
424 113 : if (peerDisplayName.empty()) {
425 0 : if (const auto* hdr = static_cast<const pjsip_contact_hdr*>(
426 0 : pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, nullptr))) {
427 0 : peerDisplayName = sip_utils::parseDisplayName(hdr);
428 : }
429 : }
430 :
431 113 : call->setPeerNumber(peerNumber);
432 113 : call->setPeerUri(account->getToUri(peerNumber));
433 113 : call->setPeerDisplayName(peerDisplayName);
434 113 : call->setState(Call::ConnectionState::PROGRESSING);
435 113 : call->getSDP().setPublishedIP(addrSdp);
436 113 : call->setPeerAllowMethods(sip_utils::getPeerAllowMethods(rdata));
437 :
438 : // Set the temporary media list. Might change when we receive
439 : // the accept from the client.
440 113 : if (r_sdp != nullptr) {
441 113 : call->getSDP().setReceivedOffer(r_sdp);
442 : }
443 :
444 113 : pjsip_dialog* dialog = nullptr;
445 113 : if (pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, nullptr, &dialog) != PJ_SUCCESS) {
446 0 : JAMI_ERROR("Unable to create UAS");
447 0 : call.reset();
448 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, nullptr, nullptr, nullptr);
449 0 : return PJ_FALSE;
450 : }
451 :
452 113 : pjsip_tpselector tp_sel = SIPVoIPLink::getTransportSelector(transport->get());
453 113 : if (!dialog or pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
454 0 : JAMI_ERROR("Unable to set transport for dialog");
455 0 : if (dialog)
456 0 : pjsip_dlg_dec_lock(dialog);
457 0 : return PJ_FALSE;
458 : }
459 :
460 113 : pjsip_inv_session* inv = nullptr;
461 : // Create UAS for the invite.
462 : // The SDP is not set here, it will be done when the call is
463 : // accepted and the media attributes of the answer are known.
464 113 : pjsip_inv_create_uas(dialog, rdata, NULL, PJSIP_INV_SUPPORT_ICE, &inv);
465 113 : if (!inv) {
466 0 : JAMI_ERROR("Call invite is not initialized");
467 0 : pjsip_dlg_dec_lock(dialog);
468 0 : return PJ_FALSE;
469 : }
470 :
471 : // dialog is now owned by invite
472 113 : pjsip_dlg_dec_lock(dialog);
473 :
474 113 : inv->mod_data[mod_ua_.id] = call.get();
475 113 : call->setInviteSession(inv);
476 :
477 : // Check whether Replaces header is present in the request and process accordingly.
478 : pjsip_dialog* replaced_dlg;
479 : pjsip_tx_data* response;
480 :
481 113 : if (pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response) != PJ_SUCCESS) {
482 0 : JAMI_ERROR("Something wrong with Replaces request.");
483 0 : call.reset();
484 :
485 : // Something wrong with the Replaces header.
486 0 : if (response) {
487 : pjsip_response_addr res_addr;
488 0 : pjsip_get_response_addr(response->pool, rdata, &res_addr);
489 0 : pjsip_endpt_send_response(endpt_, &res_addr, response, NULL, NULL);
490 : } else {
491 0 : try_respond_stateless(endpt_, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL);
492 : }
493 :
494 0 : return PJ_FALSE;
495 : }
496 :
497 : // Check if call has been transferred
498 113 : pjsip_tx_data* tdata = nullptr;
499 :
500 113 : if (pjsip_inv_initial_answer(call->inviteSession_.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata) != PJ_SUCCESS) {
501 0 : JAMI_ERROR("Unable to create answer TRYING");
502 0 : return PJ_FALSE;
503 : }
504 :
505 : // Add user-agent header
506 113 : sip_utils::addUserAgentHeader(account->getUserAgentName(), tdata);
507 :
508 113 : if (pjsip_inv_send_msg(call->inviteSession_.get(), tdata) != PJ_SUCCESS) {
509 0 : JAMI_ERROR("Unable to send msg TRYING");
510 0 : return PJ_FALSE;
511 : }
512 :
513 113 : call->setState(Call::ConnectionState::TRYING);
514 :
515 113 : if (pjsip_inv_answer(call->inviteSession_.get(), PJSIP_SC_RINGING, NULL, NULL, &tdata) != PJ_SUCCESS) {
516 0 : JAMI_ERROR("Unable to create answer RINGING");
517 0 : return PJ_FALSE;
518 : }
519 :
520 113 : sip_utils::addContactHeader(call->getContactHeader(), tdata);
521 113 : if (pjsip_inv_send_msg(call->inviteSession_.get(), tdata) != PJ_SUCCESS) {
522 0 : JAMI_ERROR("Unable to send msg RINGING");
523 0 : return PJ_FALSE;
524 : }
525 :
526 113 : call->setState(Call::ConnectionState::RINGING);
527 :
528 113 : Manager::instance().incomingCall(account->getAccountID(), *call);
529 :
530 113 : if (replaced_dlg) {
531 : // Get the INVITE session associated with the replaced dialog.
532 0 : auto* replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
533 :
534 : // Disconnect the "replaced" INVITE session.
535 0 : if (pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, nullptr, &tdata) == PJ_SUCCESS && tdata) {
536 0 : pjsip_inv_send_msg(replaced_inv, tdata);
537 : }
538 :
539 : // Close call at application level
540 0 : if (auto replacedCall = getCallFromInvite(replaced_inv))
541 0 : replacedCall->hangup(PJSIP_SC_OK);
542 : }
543 :
544 113 : return PJ_FALSE;
545 118 : }
546 :
547 : static void
548 405 : tp_state_callback(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
549 : {
550 405 : if (auto& broker = Manager::instance().sipVoIPLink().sipTransportBroker)
551 405 : broker->transportStateChanged(tp, state, info);
552 : else
553 0 : JAMI_ERROR("SIPVoIPLink with invalid SipTransportBroker");
554 405 : }
555 :
556 : /*************************************************************************************************/
557 :
558 : pjsip_endpoint*
559 12 : SIPVoIPLink::getEndpoint()
560 : {
561 12 : return endpt_;
562 : }
563 :
564 : pjsip_module*
565 0 : SIPVoIPLink::getMod()
566 : {
567 0 : return &mod_ua_;
568 : }
569 :
570 : pj_pool_t*
571 8 : SIPVoIPLink::getPool() noexcept
572 : {
573 8 : return pool_.get();
574 : }
575 :
576 : pj_caching_pool*
577 1955 : SIPVoIPLink::getCachingPool() noexcept
578 : {
579 1955 : return &cp_;
580 : }
581 :
582 39 : SIPVoIPLink::SIPVoIPLink()
583 39 : : pool_(nullptr)
584 : {
585 : #define TRY(ret) \
586 : do { \
587 : if ((ret) != PJ_SUCCESS) \
588 : throw VoipLinkException(#ret " failed"); \
589 : } while (0)
590 :
591 39 : pj_caching_pool_init(&cp_, &pj_pool_factory_default_policy, 0);
592 39 : pool_.reset(pj_pool_create(&cp_.factory, PACKAGE, static_cast<long>(64) * 1024, 4096, nullptr));
593 39 : if (!pool_)
594 0 : throw VoipLinkException("UserAgent: Unable to initialize memory pool");
595 :
596 39 : TRY(pjsip_endpt_create(&cp_.factory, pj_gethostname()->ptr, &endpt_));
597 :
598 39 : auto ns = dhtnet::ip_utils::getLocalNameservers();
599 39 : if (not ns.empty()) {
600 78 : std::vector<pj_str_t> dns_nameservers(ns.size());
601 39 : std::vector<pj_uint16_t> dns_ports(ns.size());
602 117 : for (unsigned i = 0, n = ns.size(); i < n; i++) {
603 : char hbuf[NI_MAXHOST];
604 78 : if (auto ret
605 78 : = getnameinfo((sockaddr*) &ns[i], ns[i].getLength(), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST)) {
606 0 : JAMI_WARNING("Error printing SIP nameserver: {}", jami_gai_strerror(ret));
607 : } else {
608 78 : JAMI_LOG("Using SIP nameserver: {}", hbuf);
609 78 : pj_strdup2(pool_.get(), &dns_nameservers[i], hbuf);
610 78 : dns_ports[i] = ns[i].getPort();
611 : }
612 : }
613 : pj_dns_resolver* resv;
614 39 : if (auto ret = pjsip_endpt_create_resolver(endpt_, &resv)) {
615 0 : JAMI_WARNING("Error creating SIP DNS resolver: {}", sip_utils::sip_strerror(ret));
616 : } else {
617 39 : if (auto ret = pj_dns_resolver_set_ns(resv,
618 39 : dns_nameservers.size(),
619 39 : dns_nameservers.data(),
620 39 : dns_ports.data())) {
621 0 : JAMI_WARNING("Error setting SIP DNS servers: {}", sip_utils::sip_strerror(ret));
622 : } else {
623 39 : if (auto ret = pjsip_endpt_set_resolver(endpt_, resv)) {
624 0 : JAMI_WARNING("Error setting PJSIP DNS resolver: {}", sip_utils::sip_strerror(ret));
625 : }
626 : }
627 : }
628 39 : }
629 :
630 39 : sipTransportBroker.reset(new SipTransportBroker(endpt_));
631 :
632 39 : auto status = pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), tp_state_callback);
633 39 : if (status != PJ_SUCCESS)
634 0 : JAMI_ERROR("Unable to set transport callback: {}", sip_utils::sip_strerror(status));
635 :
636 39 : TRY(pjsip_tsx_layer_init_module(endpt_));
637 39 : TRY(pjsip_ua_init_module(endpt_, nullptr));
638 39 : TRY(pjsip_replaces_init_module(endpt_)); // See the Replaces specification in RFC 3891
639 39 : TRY(pjsip_100rel_init_module(endpt_));
640 :
641 : // Initialize and register ring module
642 39 : mod_ua_.name = sip_utils::CONST_PJ_STR(PACKAGE);
643 39 : mod_ua_.id = -1;
644 39 : mod_ua_.priority = PJSIP_MOD_PRIORITY_APPLICATION;
645 39 : mod_ua_.on_rx_request = &transaction_request_cb;
646 39 : mod_ua_.on_rx_response = &transaction_response_cb;
647 39 : TRY(pjsip_endpt_register_module(endpt_, &mod_ua_));
648 :
649 39 : mod_handover_admission_.name = sip_utils::CONST_PJ_STR("jami-handover-admission");
650 39 : mod_handover_admission_.id = -1;
651 39 : mod_handover_admission_.priority = PJSIP_MOD_PRIORITY_TSX_LAYER + 1;
652 39 : mod_handover_admission_.on_rx_request = &handover_request_cb;
653 39 : TRY(pjsip_endpt_register_module(endpt_, &mod_handover_admission_));
654 :
655 39 : TRY(pjsip_evsub_init_module(endpt_));
656 39 : TRY(pjsip_xfer_init_module(endpt_));
657 :
658 : // presence/publish management
659 39 : TRY(pjsip_pres_init_module(endpt_, pjsip_evsub_instance()));
660 39 : TRY(pjsip_endpt_register_module(endpt_, &PresSubServer::mod_presence_server));
661 :
662 : static const pjsip_inv_callback inv_cb = {
663 : invite_session_state_changed_cb,
664 : outgoing_request_forked_cb,
665 : transaction_state_changed_cb,
666 : nullptr /* on_rx_offer */,
667 : on_rx_offer2,
668 : reinvite_received_cb,
669 : sdp_create_offer_cb,
670 : sdp_media_update_cb,
671 : nullptr /* on_send_ack */,
672 : nullptr /* on_redirected */,
673 : };
674 39 : TRY(pjsip_inv_usage_init(endpt_, &inv_cb));
675 :
676 : static constexpr pj_str_t allowed[] = {
677 : CONST_PJ_STR(sip_utils::SIP_METHODS::INFO),
678 : CONST_PJ_STR(sip_utils::SIP_METHODS::OPTIONS),
679 : CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE),
680 : CONST_PJ_STR(sip_utils::SIP_METHODS::PUBLISH),
681 : };
682 :
683 39 : pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ALLOW, nullptr, PJ_ARRAY_SIZE(allowed), allowed);
684 :
685 : static constexpr pj_str_t text_plain = CONST_PJ_STR("text/plain");
686 39 : pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, nullptr, 1, &text_plain);
687 :
688 : static constexpr pj_str_t accepted = CONST_PJ_STR("application/sdp");
689 39 : pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, nullptr, 1, &accepted);
690 :
691 : static constexpr pj_str_t iscomposing = CONST_PJ_STR("application/im-iscomposing+xml");
692 39 : pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, nullptr, 1, &iscomposing);
693 :
694 39 : TRY(pjsip_replaces_init_module(endpt_));
695 : #undef TRY
696 :
697 78 : sipThread_ = std::thread([this] {
698 9080 : while (running_)
699 9041 : handleEvents();
700 39 : });
701 :
702 39 : JAMI_LOG("SIPVoIPLink@{}", fmt::ptr(this));
703 39 : }
704 :
705 39 : SIPVoIPLink::~SIPVoIPLink() {}
706 :
707 : void
708 39 : SIPVoIPLink::shutdown()
709 : {
710 39 : JAMI_LOG("Shutting down SIPVoIPLink@{}…", fmt::ptr(this));
711 : // Remaining calls should not happen as possible upper callbacks
712 : // may be called and another instance of SIPVoIPLink can be re-created!
713 :
714 39 : if (not Manager::instance().callFactory.empty(Call::LinkType::SIP))
715 0 : JAMI_ERROR("{} SIP calls remains!", Manager::instance().callFactory.callCount(Call::LinkType::SIP));
716 :
717 39 : sipTransportBroker->shutdown();
718 39 : pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), nullptr);
719 :
720 39 : running_ = false;
721 39 : sipThread_.join();
722 39 : pjsip_endpt_destroy(endpt_);
723 39 : pool_.reset();
724 39 : pj_caching_pool_destroy(&cp_);
725 39 : sipTransportBroker.reset();
726 :
727 39 : JAMI_LOG("SIPVoIPLink@{} shutdown successfully completed", fmt::ptr(this));
728 39 : }
729 :
730 : std::shared_ptr<SIPAccountBase>
731 15 : SIPVoIPLink::guessAccount(std::string_view userName, std::string_view server, std::string_view fromUri) const
732 : {
733 15 : JAMI_LOG("username = {}, server = {}, from = {}", userName, server, fromUri);
734 : // Attempt to find the account id from username and server name by full match
735 :
736 15 : std::shared_ptr<SIPAccountBase> result;
737 15 : std::shared_ptr<SIPAccountBase> IP2IPAccount;
738 15 : MatchRank best = MatchRank::NONE;
739 :
740 : // SIP accounts
741 42 : for (const auto& account : Manager::instance().getAllAccounts<SIPAccount>()) {
742 33 : const MatchRank match(account->matches(userName, server));
743 :
744 : // return right away if this is a full match
745 33 : if (match == MatchRank::FULL) {
746 6 : return account;
747 27 : } else if (match > best) {
748 9 : best = match;
749 9 : result = account;
750 18 : } else if (!IP2IPAccount && account->isIP2IP()) {
751 : // Allow IP2IP calls if an account exists for this type of calls
752 9 : IP2IPAccount = account;
753 : }
754 15 : }
755 :
756 9 : return result ? result : IP2IPAccount;
757 15 : }
758 :
759 : // Called from EventThread::run (not main thread)
760 : void
761 9041 : SIPVoIPLink::handleEvents()
762 : {
763 9041 : const pj_time_val timeout = {1, 0};
764 9041 : if (auto ret = pjsip_endpt_handle_events(endpt_, &timeout))
765 0 : JAMI_ERROR("pjsip_endpt_handle_events failed with error {}", sip_utils::sip_strerror(ret));
766 9041 : }
767 :
768 : void
769 0 : SIPVoIPLink::registerKeepAliveTimer(pj_timer_entry& timer, pj_time_val& delay)
770 : {
771 0 : JAMI_DEBUG("Register new keepalive timer {:d} with delay {:d}", timer.id, delay.sec);
772 :
773 0 : if (timer.id == -1)
774 0 : JAMI_WARNING("Timer already scheduled");
775 :
776 0 : switch (pjsip_endpt_schedule_timer(endpt_, &timer, &delay)) {
777 0 : case PJ_SUCCESS:
778 0 : break;
779 :
780 0 : default:
781 0 : JAMI_ERROR("Unable to schedule new timer in pjsip endpoint");
782 :
783 : /* fallthrough */
784 0 : case PJ_EINVAL:
785 0 : JAMI_ERROR("Invalid timer or delay entry");
786 0 : break;
787 :
788 0 : case PJ_EINVALIDOP:
789 0 : JAMI_ERROR("Invalid timer entry, maybe already scheduled");
790 0 : break;
791 : }
792 0 : }
793 :
794 : void
795 0 : SIPVoIPLink::cancelKeepAliveTimer(pj_timer_entry& timer)
796 : {
797 0 : pjsip_endpt_cancel_timer(endpt_, &timer);
798 0 : }
799 :
800 : ///////////////////////////////////////////////////////////////////////////////
801 : // Private functions
802 : ///////////////////////////////////////////////////////////////////////////////
803 :
804 : static std::shared_ptr<SIPCall>
805 7037 : getCallFromInvite(pjsip_inv_session* inv)
806 : {
807 7037 : if (auto* call_ptr = static_cast<SIPCall*>(inv->mod_data[mod_ua_.id]))
808 6099 : return std::static_pointer_cast<SIPCall>(call_ptr->weak_from_this().lock());
809 938 : return nullptr;
810 : }
811 :
812 : static pj_bool_t
813 1024 : handover_request_cb(pjsip_rx_data* rdata)
814 : {
815 1024 : if (not rdata or not rdata->msg_info.msg or not rdata->msg_info.cid or not rdata->msg_info.to
816 1024 : or not rdata->msg_info.from or not rdata->msg_info.to->tag.slen)
817 124 : return PJ_FALSE;
818 900 : auto method = rdata->msg_info.msg->line.req.method.id;
819 900 : if (method != PJSIP_INVITE_METHOD and method != PJSIP_OPTIONS_METHOD)
820 858 : return PJ_FALSE;
821 84 : auto* dialog = pjsip_ua_find_dialog(&rdata->msg_info.cid->id,
822 42 : &rdata->msg_info.to->tag,
823 42 : &rdata->msg_info.from->tag,
824 : PJ_TRUE);
825 42 : if (not dialog)
826 0 : return PJ_FALSE;
827 42 : auto kind = SIPCall::HandoverRequest::Pass;
828 42 : std::shared_ptr<SIPCall> call;
829 42 : if (auto* invite = pjsip_dlg_get_inv_session(dialog)) {
830 42 : call = getCallFromInvite(invite);
831 42 : if (call and call->peerSupportsHandover() and invite->state == PJSIP_INV_STATE_CONFIRMED)
832 40 : kind = SIPCall::classifyHandoverRequest(rdata, invite->role == PJSIP_ROLE_UAC, call->recoveryGeneration());
833 : }
834 42 : pjsip_dlg_dec_lock(dialog);
835 :
836 42 : switch (kind) {
837 36 : case SIPCall::HandoverRequest::Pass:
838 36 : return PJ_FALSE;
839 0 : case SIPCall::HandoverRequest::Stale:
840 0 : pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_REQUEST_PENDING, nullptr, nullptr, nullptr);
841 0 : return PJ_TRUE;
842 0 : case SIPCall::HandoverRequest::Invalid:
843 0 : pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_BAD_REQUEST, nullptr, nullptr, nullptr);
844 0 : return PJ_TRUE;
845 6 : case SIPCall::HandoverRequest::Probe:
846 6 : break;
847 : }
848 6 : auto generation = sip_utils::getHandoverGeneration(rdata);
849 6 : auto request = sip_utils::getHandoverRequest(rdata);
850 6 : if (not generation or not request or not *request) {
851 0 : pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_BAD_REQUEST, nullptr, nullptr, nullptr);
852 0 : return PJ_TRUE;
853 : }
854 6 : auto transport = Manager::instance().sipVoIPLink().sipTransportBroker->addTransport(rdata->tp_info.transport);
855 6 : runOnMainThread([w = call->weak(), transport, generation = *generation, request = *request] {
856 6 : if (auto call = w.lock()) {
857 6 : if (auto account = std::dynamic_pointer_cast<JamiAccount>(call->getAccount().lock()))
858 6 : call->followPeerTransport(transport, account->getContactHeader(transport), generation, request);
859 6 : }
860 6 : });
861 6 : pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_OK, nullptr, nullptr, nullptr);
862 6 : return PJ_TRUE;
863 42 : }
864 :
865 : static void
866 1089 : invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* ev)
867 : {
868 1089 : if (inv == nullptr or ev == nullptr) {
869 0 : throw VoipLinkException("Unexpected null pointer");
870 : }
871 :
872 1089 : auto call = getCallFromInvite(inv);
873 1089 : if (not call)
874 111 : return;
875 :
876 978 : if (ev->type != PJSIP_EVENT_TSX_STATE and ev->type != PJSIP_EVENT_TX_MSG and ev->type != PJSIP_EVENT_RX_MSG) {
877 0 : JAMI_WARNING("[call:{}] INVITE@{} state changed to {} ({}): unexpected event type {}",
878 : call->getCallId(),
879 : fmt::ptr(inv),
880 : pjsip_inv_state_name(inv->state),
881 : pjsip_inv_state_name(inv->state),
882 : static_cast<int>(ev->type));
883 0 : return;
884 : }
885 :
886 978 : decltype(pjsip_transaction::status_code) status_code = 0;
887 :
888 978 : if (ev->type == PJSIP_EVENT_TSX_STATE) {
889 774 : auto* const tsx = ev->body.tsx_state.tsx;
890 774 : status_code = tsx ? tsx->status_code : PJSIP_SC_NOT_FOUND;
891 774 : const pj_str_t* description = pjsip_get_status_text(status_code);
892 :
893 774 : JAMI_LOG("[call:{}] INVITE@{:p} state changed to {:d} ({}): cause={:d}, tsx@{:p} status {:d} ({:s})",
894 : call->getCallId(),
895 : fmt::ptr(inv),
896 : (int) inv->state,
897 : pjsip_inv_state_name(inv->state),
898 : (int) inv->cause,
899 : fmt::ptr(tsx),
900 : status_code,
901 : sip_utils::as_view(*description));
902 204 : } else if (ev->type == PJSIP_EVENT_TX_MSG) {
903 102 : JAMI_LOG("[call:{}] INVITE@{:p} state changed to {:d} ({:s}): cause={:d} (TX_MSG)",
904 : call->getCallId(),
905 : fmt::ptr(inv),
906 : (int) inv->state,
907 : pjsip_inv_state_name(inv->state),
908 : (int) inv->cause);
909 : }
910 978 : pjsip_rx_data* rdata {nullptr};
911 978 : if (ev->type == PJSIP_EVENT_RX_MSG) {
912 102 : rdata = ev->body.rx_msg.rdata;
913 876 : } else if (ev->type == PJSIP_EVENT_TSX_STATE and ev->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
914 322 : rdata = ev->body.tsx_state.src.rdata;
915 : }
916 978 : if (rdata != nullptr) {
917 424 : call->setPeerUaVersion(sip_utils::getPeerUserAgent(rdata));
918 424 : if (sip_utils::supportsCallHandover(rdata) && std::dynamic_pointer_cast<JamiAccount>(call->getAccount().lock()))
919 94 : call->setPeerHandoverSupported(true);
920 424 : auto methods = sip_utils::getPeerAllowMethods(rdata);
921 424 : if (not methods.empty()) {
922 219 : call->setPeerAllowMethods(std::move(methods));
923 : }
924 424 : }
925 :
926 978 : switch (inv->state) {
927 225 : case PJSIP_INV_STATE_EARLY:
928 225 : if (status_code == PJSIP_SC_RINGING)
929 225 : call->onPeerRinging();
930 225 : break;
931 :
932 204 : case PJSIP_INV_STATE_CONFIRMED:
933 : // After we sent or received a ACK - The connection is established
934 204 : call->onAnswered();
935 204 : break;
936 :
937 117 : case PJSIP_INV_STATE_DISCONNECTED:
938 117 : switch (inv->cause) {
939 : // When a peer's device replies busy
940 2 : case PJSIP_SC_BUSY_HERE:
941 2 : call->onBusyHere();
942 2 : break;
943 : // When the peer manually refuse the call
944 4 : case PJSIP_SC_DECLINE:
945 : case PJSIP_SC_BUSY_EVERYWHERE:
946 4 : if (inv->role != PJSIP_ROLE_UAC)
947 2 : break;
948 : // close call
949 2 : call->onClosed();
950 2 : break;
951 : // The call terminates normally - BYE / CANCEL
952 109 : case PJSIP_SC_OK:
953 : case PJSIP_SC_REQUEST_TERMINATED:
954 109 : call->onClosed();
955 109 : break;
956 :
957 : // Error/unhandled conditions
958 2 : default:
959 2 : call->onFailure(inv->cause);
960 2 : break;
961 : }
962 117 : break;
963 :
964 432 : default:
965 432 : break;
966 : }
967 1089 : }
968 :
969 : static void
970 36 : on_rx_offer2(pjsip_inv_session* inv, struct pjsip_inv_on_rx_offer_cb_param* param)
971 : {
972 36 : if (not param or not param->offer) {
973 0 : JAMI_ERROR("Invalid offer");
974 0 : return;
975 : }
976 :
977 36 : auto call = getCallFromInvite(inv);
978 36 : if (not call)
979 0 : return;
980 :
981 : // This callback is called whenever a new media offer is found in a
982 : // SIP message, typically in a re-invite and in a '200 OK' (as a
983 : // response to an empty invite).
984 : // Here we only handle the second case. The first case is handled
985 : // in reinvite_received_cb.
986 36 : if (inv->cause != PJSIP_SC_OK) {
987 : // Silently ignore if it's not a '200 OK'
988 36 : return;
989 : }
990 :
991 0 : if (auto call = getCallFromInvite(inv)) {
992 0 : if (auto const& account = call->getAccount().lock()) {
993 0 : call->onReceiveOfferIn200OK(param->offer);
994 0 : }
995 0 : }
996 36 : }
997 :
998 : static pj_status_t
999 36 : reinvite_received_cb(pjsip_inv_session* inv, const pjmedia_sdp_session* offer, pjsip_rx_data* rdata)
1000 : {
1001 36 : if (!offer)
1002 0 : return !PJ_SUCCESS;
1003 36 : if (auto call = getCallFromInvite(inv)) {
1004 36 : if (auto const& account = call->getAccount().lock()) {
1005 36 : return call->onReceiveReinvite(offer, rdata);
1006 36 : }
1007 36 : }
1008 :
1009 : // Return success if there is no matching call. The re-invite
1010 : // should be ignored.
1011 0 : return PJ_SUCCESS;
1012 : }
1013 :
1014 : static void
1015 0 : sdp_create_offer_cb(pjsip_inv_session* inv, pjmedia_sdp_session** p_offer)
1016 : {
1017 0 : auto call = getCallFromInvite(inv);
1018 0 : if (not call)
1019 0 : return;
1020 :
1021 0 : auto account = call->getSIPAccount();
1022 0 : if (not account) {
1023 0 : JAMI_ERROR("No account detected");
1024 0 : return;
1025 : }
1026 :
1027 0 : if (account->isEmptyOffersEnabled()) {
1028 : // Skip if the client wants to send an empty offer.
1029 0 : JAMI_LOG("Client requested to send an empty offer (no SDP)");
1030 0 : return;
1031 : }
1032 :
1033 0 : auto family = pj_AF_UNSPEC();
1034 : // Use the same address family as the SIP transport
1035 0 : if (auto* dlg = inv->dlg) {
1036 0 : if (dlg->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
1037 0 : if (auto* tr = dlg->tp_sel.u.transport)
1038 0 : family = tr->local_addr.addr.sa_family;
1039 0 : } else if (dlg->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) {
1040 0 : if (auto* tr = dlg->tp_sel.u.listener)
1041 0 : family = tr->local_addr.addr.sa_family;
1042 : }
1043 : }
1044 : // If transport family is unknown, detect from available addresses
1045 0 : if (family == pj_AF_UNSPEC()) {
1046 0 : auto addr4 = dhtnet::ip_utils::getInterfaceAddr(account->getLocalInterface(), pj_AF_INET());
1047 0 : family = addr4 ? pj_AF_INET() : pj_AF_INET6();
1048 : }
1049 0 : auto ifaceAddr = dhtnet::ip_utils::getInterfaceAddr(account->getLocalInterface(), family);
1050 :
1051 0 : dhtnet::IpAddr address;
1052 0 : if (account->getUPnPActive()) {
1053 : /* use UPnP addr, or published addr if it's set */
1054 0 : address = account->getPublishedSameasLocal() ? account->getUPnPIpAddress() : account->getPublishedIpAddress();
1055 : } else {
1056 0 : address = account->getPublishedSameasLocal() ? ifaceAddr : account->getPublishedIpAddress();
1057 : }
1058 :
1059 : /* fallback on local address */
1060 0 : if (not address)
1061 0 : address = ifaceAddr;
1062 :
1063 0 : auto& sdp = call->getSDP();
1064 0 : sdp.setPublishedIP(address);
1065 :
1066 : // This list should be provided by the client. Kept for backward compatibility.
1067 0 : auto const& mediaList = call->getMediaAttributeList();
1068 0 : if (mediaList.empty()) {
1069 0 : throw VoipLinkException("Unexpected empty media attribute list");
1070 : }
1071 :
1072 0 : JAMI_LOG("Creating a SDP offer using the following media:");
1073 0 : for (auto const& media : mediaList) {
1074 0 : JAMI_LOG("[call {}] Media {}", call->getCallId(), media.toString(true));
1075 : }
1076 :
1077 0 : const bool created = sdp.createOffer(mediaList);
1078 :
1079 0 : if (created and p_offer != nullptr)
1080 0 : *p_offer = sdp.getLocalSdpSession();
1081 0 : }
1082 :
1083 : static const pjmedia_sdp_session*
1084 269 : get_active_remote_sdp(pjsip_inv_session* inv)
1085 : {
1086 269 : const pjmedia_sdp_session* sdp_session {};
1087 :
1088 269 : if (pjmedia_sdp_neg_get_active_remote(inv->neg, &sdp_session) != PJ_SUCCESS) {
1089 0 : JAMI_ERROR("Active remote not present");
1090 0 : return nullptr;
1091 : }
1092 :
1093 269 : if (pjmedia_sdp_validate(sdp_session) != PJ_SUCCESS) {
1094 0 : JAMI_ERROR("Invalid remote SDP session");
1095 0 : return nullptr;
1096 : }
1097 :
1098 269 : return sdp_session;
1099 : }
1100 :
1101 : static const pjmedia_sdp_session*
1102 269 : get_active_local_sdp(pjsip_inv_session* inv)
1103 : {
1104 269 : const pjmedia_sdp_session* sdp_session {};
1105 :
1106 269 : if (pjmedia_sdp_neg_get_active_local(inv->neg, &sdp_session) != PJ_SUCCESS) {
1107 0 : JAMI_ERROR("Active local not present");
1108 0 : return nullptr;
1109 : }
1110 :
1111 269 : if (pjmedia_sdp_validate(sdp_session) != PJ_SUCCESS) {
1112 0 : JAMI_ERROR("Invalid local SDP session");
1113 0 : return nullptr;
1114 : }
1115 :
1116 269 : return sdp_session;
1117 : }
1118 :
1119 : // This callback is called after SDP offer/answer session has completed.
1120 : static void
1121 270 : sdp_media_update_cb(pjsip_inv_session* inv, pj_status_t status)
1122 : {
1123 270 : auto call = getCallFromInvite(inv);
1124 270 : if (not call or (call->peerSupportsHandover() and not call->isCurrentMediaNegotiation()))
1125 0 : return;
1126 :
1127 270 : JAMI_LOG("[call:{}] INVITE@{} media update: status {}", call->getCallId(), fmt::ptr(inv), status);
1128 :
1129 270 : if (status != PJ_SUCCESS) {
1130 1 : const int reason = inv->state != PJSIP_INV_STATE_NULL and inv->state != PJSIP_INV_STATE_CONFIRMED
1131 2 : ? PJSIP_SC_UNSUPPORTED_MEDIA_TYPE
1132 : : 0;
1133 :
1134 1 : JAMI_WARNING("[call:{}] SDP offer failed, reason {}", call->getCallId(), reason);
1135 :
1136 1 : call->hangup(reason);
1137 1 : return;
1138 : }
1139 :
1140 : // Fetch SDP data from request
1141 269 : const auto* const localSDP = get_active_local_sdp(inv);
1142 269 : const auto* const remoteSDP = get_active_remote_sdp(inv);
1143 :
1144 : // Update our SDP manager
1145 269 : auto& sdp = call->getSDP();
1146 269 : sdp.setActiveLocalSdpSession(localSDP);
1147 269 : if (localSDP != nullptr) {
1148 269 : Sdp::printSession(localSDP, "Local active session:", sdp.getSdpDirection());
1149 : }
1150 :
1151 269 : sdp.setActiveRemoteSdpSession(remoteSDP);
1152 269 : if (remoteSDP != nullptr) {
1153 269 : Sdp::printSession(remoteSDP, "Remote active session:", sdp.getSdpDirection());
1154 : }
1155 :
1156 269 : call->onMediaNegotiationComplete();
1157 270 : }
1158 :
1159 : static void
1160 0 : outgoing_request_forked_cb(pjsip_inv_session* /*inv*/, pjsip_event* /*e*/)
1161 0 : {}
1162 :
1163 : static bool
1164 351 : handleMediaControl(SIPCall& call, pjsip_msg_body* body)
1165 : {
1166 : /*
1167 : * Incoming INFO request for media control.
1168 : */
1169 351 : constexpr pj_str_t STR_APPLICATION = CONST_PJ_STR("application");
1170 351 : constexpr pj_str_t STR_MEDIA_CONTROL_XML = CONST_PJ_STR("media_control+xml");
1171 :
1172 351 : if (body->len and pj_stricmp(&body->content_type.type, &STR_APPLICATION) == 0
1173 702 : and pj_stricmp(&body->content_type.subtype, &STR_MEDIA_CONTROL_XML) == 0) {
1174 351 : auto body_msg = std::string_view((char*) body->data, (size_t) body->len);
1175 :
1176 : /* Apply and answer the INFO request */
1177 : static constexpr auto PICT_FAST_UPDATE = "picture_fast_update"sv;
1178 : static constexpr auto STREAM_ID = "stream_id"sv;
1179 : static constexpr auto DEVICE_ORIENTATION = "device_orientation"sv;
1180 : static constexpr auto RECORDING_STATE = "recording_state"sv;
1181 : static constexpr auto MUTE_STATE = "mute_state"sv;
1182 : static constexpr auto VOICE_ACTIVITY = "voice_activity"sv;
1183 :
1184 351 : int streamIdx = -1;
1185 351 : if (body_msg.find(STREAM_ID) != std::string_view::npos) {
1186 : // Note: here we use the index of the RTP stream, not its label!
1187 : // Indeed, both sides will have different labels as they have different call IDs
1188 327 : static const std::regex STREAMID_REGEX("<stream_id>([0-9]+)</stream_id>");
1189 327 : std::svmatch matched_pattern;
1190 327 : std::regex_search(body_msg, matched_pattern, STREAMID_REGEX);
1191 327 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1192 : try {
1193 327 : streamIdx = std::stoi(matched_pattern[1]);
1194 0 : } catch (const std::exception& e) {
1195 0 : JAMI_WARNING("Error parsing stream index: {}", e.what());
1196 0 : }
1197 : }
1198 327 : }
1199 :
1200 351 : if (body_msg.find(PICT_FAST_UPDATE) != std::string_view::npos) {
1201 268 : call.sendKeyframe(streamIdx);
1202 351 : return true;
1203 83 : } else if (body_msg.find(DEVICE_ORIENTATION) != std::string_view::npos) {
1204 59 : static const std::regex ORIENTATION_REGEX("device_orientation=([-+]?[0-9]+)");
1205 :
1206 59 : std::svmatch matched_pattern;
1207 59 : std::regex_search(body_msg, matched_pattern, ORIENTATION_REGEX);
1208 :
1209 59 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1210 : try {
1211 59 : int rotation = -std::stoi(matched_pattern[1]);
1212 59 : while (rotation <= -180)
1213 0 : rotation += 360;
1214 59 : while (rotation > 180)
1215 0 : rotation -= 360;
1216 59 : JAMI_WARNING("Rotate video {} deg.", rotation);
1217 : #ifdef ENABLE_VIDEO
1218 59 : call.setRotation(streamIdx, rotation);
1219 : #endif
1220 0 : } catch (const std::exception& e) {
1221 0 : JAMI_WARNING("Error parsing angle: {}", e.what());
1222 0 : }
1223 59 : return true;
1224 : }
1225 83 : } else if (body_msg.find(RECORDING_STATE) != std::string_view::npos) {
1226 19 : static const std::regex REC_REGEX("recording_state=([0-1])");
1227 19 : std::svmatch matched_pattern;
1228 19 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1229 :
1230 19 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1231 : try {
1232 19 : bool state = std::stoi(matched_pattern[1]);
1233 19 : call.peerRecording(state);
1234 0 : } catch (const std::exception& e) {
1235 0 : JAMI_WARNING("Error parsing state remote recording: {}", e.what());
1236 0 : }
1237 19 : return true;
1238 : }
1239 24 : } else if (body_msg.find(MUTE_STATE) != std::string_view::npos) {
1240 5 : static const std::regex REC_REGEX("mute_state=([0-1])");
1241 5 : std::svmatch matched_pattern;
1242 5 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1243 :
1244 5 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1245 : try {
1246 5 : bool state = std::stoi(matched_pattern[1]);
1247 5 : call.peerMuted(state, streamIdx);
1248 0 : } catch (const std::exception& e) {
1249 0 : JAMI_WARNING("Error parsing state remote mute: {}", e.what());
1250 0 : }
1251 5 : return true;
1252 : }
1253 5 : } else if (body_msg.find(VOICE_ACTIVITY) != std::string_view::npos) {
1254 0 : static const std::regex REC_REGEX("voice_activity=([0-1])");
1255 0 : std::svmatch matched_pattern;
1256 0 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1257 :
1258 0 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1259 : try {
1260 0 : bool state = std::stoi(matched_pattern[1]);
1261 0 : call.peerVoice(state);
1262 0 : } catch (const std::exception& e) {
1263 0 : JAMI_WARNING("Error parsing state remote voice: {}", e.what());
1264 0 : }
1265 0 : return true;
1266 : }
1267 0 : }
1268 : }
1269 :
1270 0 : return false;
1271 : }
1272 :
1273 : /**
1274 : * Helper function to process refer function on call transfer
1275 : */
1276 : static bool
1277 2 : transferCall(SIPCall& call, const std::string& refer_to)
1278 : {
1279 2 : const auto& callId = call.getCallId();
1280 2 : JAMI_WARNING("[call:{}] Attempting to transfer to {}", callId, refer_to);
1281 : try {
1282 4 : Manager::instance().newOutgoingCall(refer_to,
1283 4 : call.getAccountId(),
1284 4 : MediaAttribute::mediaAttributesToMediaMaps(call.getMediaAttributeList()));
1285 2 : Manager::instance().hangupCall(call.getAccountId(), callId);
1286 0 : } catch (const std::exception& e) {
1287 0 : JAMI_ERROR("[call:{}] SIP transfer failed: {}", callId, e.what());
1288 0 : return false;
1289 0 : }
1290 2 : return true;
1291 : }
1292 :
1293 : static void
1294 353 : replyToRequest(pjsip_inv_session* inv, pjsip_rx_data* rdata, int status_code)
1295 : {
1296 353 : const auto ret = pjsip_dlg_respond(inv->dlg, rdata, status_code, nullptr, nullptr, nullptr);
1297 353 : if (ret != PJ_SUCCESS)
1298 0 : JAMI_WARNING("SIP: Failed to reply {} to request", status_code);
1299 353 : }
1300 :
1301 : static void
1302 2 : onRequestRefer(pjsip_inv_session* inv, pjsip_rx_data* rdata, pjsip_msg* msg, SIPCall& call)
1303 : {
1304 : static constexpr pj_str_t str_refer_to = CONST_PJ_STR("Refer-To");
1305 :
1306 2 : if (auto* refer_to = static_cast<pjsip_generic_string_hdr*>(
1307 2 : pjsip_msg_find_hdr_by_name(msg, &str_refer_to, nullptr))) {
1308 : // RFC 3515, 2.4.2: reply bad request if no or too many refer-to header.
1309 4 : if (static_cast<void*>(refer_to->next) == static_cast<void*>(&msg->hdr)
1310 2 : or !pjsip_msg_find_hdr_by_name(msg, &str_refer_to, refer_to->next)) {
1311 2 : replyToRequest(inv, rdata, PJSIP_SC_ACCEPTED);
1312 2 : transferCall(call, std::string(refer_to->hvalue.ptr, refer_to->hvalue.slen));
1313 :
1314 : // RFC 3515, 2.4.4: we MUST handle the processing using NOTIFY msgs
1315 : // But your current design doesn't permit that
1316 2 : return;
1317 : } else
1318 0 : JAMI_ERROR("[call:{}] REFER: too many Refer-To headers", call.getCallId());
1319 : } else
1320 0 : JAMI_ERROR("[call:{}] REFER: no Refer-To header", call.getCallId());
1321 :
1322 0 : replyToRequest(inv, rdata, PJSIP_SC_BAD_REQUEST);
1323 : }
1324 :
1325 : static void
1326 351 : onRequestInfo(pjsip_inv_session* inv, pjsip_rx_data* rdata, pjsip_msg* msg, SIPCall& call)
1327 : {
1328 351 : if (!msg->body or handleMediaControl(call, msg->body))
1329 351 : replyToRequest(inv, rdata, PJSIP_SC_OK);
1330 351 : }
1331 :
1332 : static void
1333 0 : onRequestNotify(pjsip_inv_session* /*inv*/, pjsip_rx_data* /*rdata*/, pjsip_msg* msg, SIPCall& call)
1334 : {
1335 0 : if (!msg->body)
1336 0 : return;
1337 :
1338 0 : const std::string_view bodyText {static_cast<char*>(msg->body->data), msg->body->len};
1339 0 : JAMI_LOG("[call:{}] NOTIFY body start - {}\n{}\n[call:{}] NOTIFY body end - {}",
1340 : call.getCallId(),
1341 : fmt::ptr(msg->body),
1342 : bodyText,
1343 : call.getCallId(),
1344 : fmt::ptr(msg->body));
1345 :
1346 : // TODO
1347 : }
1348 :
1349 : static void
1350 5564 : transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjsip_event* event)
1351 : {
1352 5564 : auto call = getCallFromInvite(inv);
1353 5564 : if (not call)
1354 827 : return;
1355 :
1356 4737 : if (tsx->method.id == PJSIP_INVITE_METHOD
1357 1124 : and (tsx->state == PJSIP_TSX_STATE_TERMINATED or tsx->state == PJSIP_TSX_STATE_CONFIRMED)) {
1358 268 : if (tsx->state == PJSIP_TSX_STATE_TERMINATED and tsx->status_code == PJSIP_SC_REQUEST_PENDING
1359 0 : and tsx->role == PJSIP_ROLE_UAC and tsx->last_tx) {
1360 0 : if (auto generation = sip_utils::getHandoverGeneration(tsx->last_tx->msg))
1361 0 : call->retryRecoveryOffer(*generation);
1362 : }
1363 268 : runOnMainThread([wcall = std::weak_ptr(call)] {
1364 268 : if (auto call = wcall.lock())
1365 268 : call->onInviteTransactionEnded();
1366 268 : });
1367 : }
1368 :
1369 : #ifdef DEBUG_SIP_REQUEST_MSG
1370 : processInviteResponseHelper(inv, event);
1371 : #endif
1372 :
1373 : // We process here only incoming request message
1374 4737 : if (tsx->role != PJSIP_ROLE_UAS or tsx->state != PJSIP_TSX_STATE_TRYING
1375 762 : or event->body.tsx_state.type != PJSIP_EVENT_RX_MSG) {
1376 3975 : return;
1377 : }
1378 :
1379 762 : auto* const rdata = event->body.tsx_state.src.rdata;
1380 762 : if (!rdata) {
1381 0 : JAMI_ERROR("[INVITE:{:p}] SIP RX request without rx data", fmt::ptr(inv));
1382 0 : return;
1383 : }
1384 :
1385 762 : auto* const msg = rdata->msg_info.msg;
1386 762 : if (msg->type != PJSIP_REQUEST_MSG) {
1387 0 : JAMI_ERROR("[INVITE:{:p}] SIP RX request without msg", fmt::ptr(inv));
1388 0 : return;
1389 : }
1390 :
1391 : // Using method name to dispatch
1392 762 : auto methodName = sip_utils::as_view(msg->line.req.method.name);
1393 762 : JAMI_LOG("[INVITE:{:p}] RX SIP method {:d} ({:s})", fmt::ptr(inv), (int) msg->line.req.method.id, methodName);
1394 :
1395 : #ifdef DEBUG_SIP_REQUEST_MSG
1396 : char msgbuf[1000];
1397 : auto msgsize = pjsip_msg_print(msg, msgbuf, sizeof msgbuf);
1398 : if (msgsize > 0)
1399 : JAMI_LOG("{:s}", std::string_view(msgbuf, msgsize));
1400 : #endif // DEBUG_SIP_REQUEST_MSG
1401 :
1402 762 : if (methodName == sip_utils::SIP_METHODS::REFER)
1403 2 : onRequestRefer(inv, rdata, msg, *call);
1404 760 : else if (methodName == sip_utils::SIP_METHODS::INFO)
1405 351 : onRequestInfo(inv, rdata, msg, *call);
1406 409 : else if (methodName == sip_utils::SIP_METHODS::NOTIFY)
1407 0 : onRequestNotify(inv, rdata, msg, *call);
1408 409 : else if (methodName == sip_utils::SIP_METHODS::OPTIONS)
1409 0 : handleIncomingOptions(rdata);
1410 409 : else if (methodName == sip_utils::SIP_METHODS::MESSAGE) {
1411 264 : if (msg->body)
1412 528 : runOnMainThread([call, m = im::parseSipMessage(msg)]() mutable { call->onTextMessage(std::move(m)); });
1413 : }
1414 5564 : }
1415 :
1416 : #ifdef DEBUG_SIP_REQUEST_MSG
1417 : static void
1418 : processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* event)
1419 : {
1420 : if (event->body.tsx_state.type != PJSIP_EVENT_RX_MSG)
1421 : return;
1422 :
1423 : const auto rdata = event->body.tsx_state.src.rdata;
1424 : if (rdata == nullptr or rdata->msg_info.msg == nullptr)
1425 : return;
1426 :
1427 : const auto msg = rdata->msg_info.msg;
1428 : if (msg->type != PJSIP_RESPONSE_MSG)
1429 : return;
1430 :
1431 : // Only handle the following responses
1432 : switch (msg->line.status.code) {
1433 : case PJSIP_SC_TRYING:
1434 : case PJSIP_SC_RINGING:
1435 : case PJSIP_SC_OK:
1436 : break;
1437 : default:
1438 : return;
1439 : }
1440 :
1441 : JAMI_LOG("[INVITE:{:p}] SIP RX response: reason {:s}, status code {:d} {:s}",
1442 : fmt::ptr(inv),
1443 : sip_utils::as_view(msg->line.status.reason),
1444 : msg->line.status.code,
1445 : sip_utils::as_view(*pjsip_get_status_text(msg->line.status.code)));
1446 :
1447 : sip_utils::logMessageHeaders(&msg->hdr);
1448 : }
1449 : #endif
1450 :
1451 : int
1452 782 : SIPVoIPLink::getModId()
1453 : {
1454 782 : return mod_ua_.id;
1455 : }
1456 :
1457 : void
1458 0 : SIPVoIPLink::createSDPOffer(pjsip_inv_session* inv)
1459 : {
1460 0 : if (inv == nullptr) {
1461 0 : throw VoipLinkException("Invite session is unable to be null");
1462 : }
1463 0 : sdp_create_offer_cb(inv, nullptr);
1464 0 : }
1465 :
1466 : // Thread-safe DNS resolver callback mapping
1467 : class SafeResolveCallbackMap
1468 : {
1469 : public:
1470 : using ResolveCallback = std::function<void(pj_status_t, const pjsip_server_addresses*)>;
1471 :
1472 5 : void registerCallback(uintptr_t key, ResolveCallback&& cb)
1473 : {
1474 5 : std::lock_guard lk(mutex_);
1475 5 : cbMap_.emplace(key, std::move(cb));
1476 5 : }
1477 :
1478 5 : void process(uintptr_t key, pj_status_t status, const pjsip_server_addresses* addr)
1479 : {
1480 5 : std::lock_guard lk(mutex_);
1481 5 : auto it = cbMap_.find(key);
1482 5 : if (it != cbMap_.end()) {
1483 5 : it->second(status, addr);
1484 5 : cbMap_.erase(it);
1485 : }
1486 5 : }
1487 :
1488 : private:
1489 : std::mutex mutex_;
1490 : std::map<uintptr_t, ResolveCallback> cbMap_;
1491 : };
1492 :
1493 : static SafeResolveCallbackMap&
1494 10 : getResolveCallbackMap()
1495 : {
1496 10 : static SafeResolveCallbackMap map;
1497 10 : return map;
1498 : }
1499 :
1500 : static void
1501 5 : resolver_callback(pj_status_t status, void* token, const struct pjsip_server_addresses* addr)
1502 : {
1503 5 : getResolveCallbackMap().process((uintptr_t) token, status, addr);
1504 5 : }
1505 :
1506 : void
1507 5 : SIPVoIPLink::resolveSrvName(const std::string& name, pjsip_transport_type_e type, SrvResolveCallback&& cb)
1508 : {
1509 : // PJSIP limits hostname to be longer than PJ_MAX_HOSTNAME.
1510 : // But, resolver prefix the given name by a string like "_sip._udp."
1511 : // causing a check against PJ_MAX_HOSTNAME to be useless.
1512 : // It's not easy to pre-determinate as it's implementation dependent.
1513 : // So we just choose a security marge enough for most cases, preventing a crash later
1514 : // in the call of pjsip_endpt_resolve().
1515 5 : if (name.length() > (PJ_MAX_HOSTNAME - 12)) {
1516 0 : JAMI_ERROR("Hostname is too long");
1517 0 : cb({});
1518 0 : return;
1519 : }
1520 :
1521 : // extract port if name is in form "server:port"
1522 : int port;
1523 : pj_ssize_t name_size;
1524 5 : const auto n = name.rfind(':');
1525 5 : if (n != std::string::npos) {
1526 0 : port = std::atoi(name.c_str() + n + 1);
1527 0 : name_size = static_cast<pj_ssize_t>(n);
1528 : } else {
1529 5 : port = 0;
1530 5 : name_size = static_cast<pj_ssize_t>(name.size());
1531 : }
1532 5 : JAMI_LOG("Attempt to resolve '{}' (port: {})", name, port);
1533 :
1534 5 : pjsip_host_info host_info {
1535 : /*.flag = */ 0,
1536 : /*.type = */ type,
1537 5 : /*.addr = */ {{(char*) name.c_str(), name_size}, port},
1538 5 : };
1539 :
1540 5 : const auto token = std::hash<std::string>()(name + std::to_string(type));
1541 5 : getResolveCallbackMap()
1542 5 : .registerCallback(token, [=, cb = std::move(cb)](pj_status_t s, const pjsip_server_addresses* r) {
1543 : try {
1544 5 : if (s != PJ_SUCCESS || !r) {
1545 0 : JAMI_WARNING("Unable to resolve \"{}\" using pjsip_endpt_resolve, attempting getaddrinfo.", name);
1546 0 : dht::ThreadPool::io().run([=, cb = std::move(cb)]() {
1547 0 : auto ips = dhtnet::ip_utils::getAddrList(name.c_str());
1548 0 : runOnMainThread(std::bind(cb, std::move(ips)));
1549 0 : });
1550 0 : } else {
1551 5 : std::vector<dhtnet::IpAddr> ips;
1552 5 : ips.reserve(r->count);
1553 10 : for (unsigned i = 0; i < r->count; i++)
1554 5 : ips.push_back(r->entry[i].addr);
1555 5 : cb(ips);
1556 5 : }
1557 0 : } catch (const std::exception& e) {
1558 0 : JAMI_ERROR("Error resolving address: {}", e.what());
1559 0 : cb({});
1560 0 : }
1561 5 : });
1562 :
1563 5 : pjsip_endpt_resolve(endpt_, pool_.get(), &host_info, (void*) token, resolver_callback);
1564 : }
1565 :
1566 : // Workaround for MSVC where __VA_ARGS__ is treated as a single token
1567 : // when passed to another variadic macro. This forces a re-scan.
1568 : #define VA_EXPAND(x) x
1569 :
1570 : #define RETURN_IF_NULL(A, ...) \
1571 : if ((A) == NULL) { \
1572 : VA_EXPAND(JAMI_WARNING(__VA_ARGS__)); \
1573 : return; \
1574 : }
1575 :
1576 : #define RETURN_FALSE_IF_NULL(A, ...) \
1577 : if ((A) == NULL) { \
1578 : VA_EXPAND(JAMI_WARNING(__VA_ARGS__)); \
1579 : return false; \
1580 : }
1581 :
1582 : void
1583 30 : SIPVoIPLink::findLocalAddressFromTransport(pjsip_transport* transport,
1584 : pjsip_transport_type_e transportType,
1585 : const std::string& host,
1586 : std::string& addr,
1587 : pj_uint16_t& port) const
1588 : {
1589 : // Initialize the SIP port with the default SIP port
1590 30 : port = pjsip_transport_get_default_port_for_type(transportType);
1591 :
1592 : // Initialize the SIP address with the hostname
1593 30 : addr = sip_utils::as_view(*pj_gethostname());
1594 :
1595 : // Update address and port with active transport
1596 30 : RETURN_IF_NULL(transport, "Transport is NULL in findLocalAddress, using local address {} :{}", addr, port);
1597 :
1598 : // get the transport manager associated with the SIP enpoint
1599 30 : auto* tpmgr = pjsip_endpt_get_tpmgr(endpt_);
1600 30 : RETURN_IF_NULL(tpmgr, "Transport manager is NULL in findLocalAddress, using local address {} :{}", addr, port);
1601 :
1602 30 : const pj_str_t pjstring(CONST_PJ_STR(host));
1603 :
1604 30 : auto tp_sel = getTransportSelector(transport);
1605 30 : pjsip_tpmgr_fla2_param param = {transportType, &tp_sel, pjstring, PJ_FALSE, {nullptr, 0}, 0, nullptr};
1606 30 : if (pjsip_tpmgr_find_local_addr2(tpmgr, pool_.get(), ¶m) != PJ_SUCCESS) {
1607 0 : JAMI_WARNING("Unable to retrieve local address and port from transport, using {} :{}", addr, port);
1608 0 : return;
1609 : }
1610 :
1611 : // Update local address based on the transport type
1612 30 : addr = sip_utils::as_view(param.ret_addr);
1613 :
1614 : // Determine the local port based on transport information
1615 30 : port = param.ret_port;
1616 : }
1617 :
1618 : bool
1619 0 : SIPVoIPLink::findLocalAddressFromSTUN(
1620 : pjsip_transport* transport, pj_str_t* stunServerName, int stunPort, std::string& addr, pj_uint16_t& port) const
1621 : {
1622 : // WARN: pjstun_get_mapped_addr2 works in IPv4 only.
1623 : // For IPv6 transports, STUN is not needed (no NAT), return local address.
1624 : // WARN: this function is blocking (network request).
1625 :
1626 : // Initialize the sip port with the default SIP port
1627 0 : port = sip_utils::DEFAULT_SIP_PORT;
1628 :
1629 : // Detect transport family - skip STUN for IPv6 (no NAT on IPv6)
1630 0 : if (transport && transport->local_addr.addr.sa_family == pj_AF_INET6()) {
1631 0 : auto localIp6 = dhtnet::ip_utils::getLocalAddr(pj_AF_INET6());
1632 0 : if (localIp6) {
1633 0 : addr = localIp6.toString();
1634 0 : port = pj_sockaddr_get_port(&transport->local_addr);
1635 0 : if (port == 0)
1636 0 : port = sip_utils::DEFAULT_SIP_PORT;
1637 0 : JAMI_DEBUG("IPv6 transport detected, skipping STUN, using local address {}:{}", addr, port);
1638 0 : return true;
1639 : }
1640 : }
1641 :
1642 : // Get Local IPv4 address for STUN
1643 0 : auto localIp = dhtnet::ip_utils::getLocalAddr(pj_AF_INET());
1644 0 : if (not localIp) {
1645 0 : JAMI_WARNING("Failed to find local IP");
1646 0 : return false;
1647 : }
1648 :
1649 0 : addr = localIp.toString();
1650 :
1651 : // Update address and port with active transport
1652 0 : RETURN_FALSE_IF_NULL(transport, "Transport is NULL in findLocalAddress, using local address {}:{}", addr, port);
1653 :
1654 0 : JAMI_LOG("STUN mapping of '{}:{}'", addr, port);
1655 :
1656 : pj_sockaddr_in mapped_addr;
1657 0 : pj_sock_t sipSocket = pjsip_udp_transport_get_socket(transport);
1658 0 : const pjstun_setting stunOpt = {PJ_TRUE, localIp.getFamily(), *stunServerName, stunPort, *stunServerName, stunPort};
1659 0 : pj_status_t stunStatus = pjstun_get_mapped_addr2(&cp_.factory, &stunOpt, 1, &sipSocket, &mapped_addr);
1660 :
1661 0 : switch (stunStatus) {
1662 0 : case PJLIB_UTIL_ESTUNNOTRESPOND:
1663 0 : JAMI_ERROR("No response from STUN server {:s}", sip_utils::as_view(*stunServerName));
1664 0 : return false;
1665 :
1666 0 : case PJLIB_UTIL_ESTUNSYMMETRIC:
1667 0 : JAMI_ERROR("Different mapped addresses are returned by servers.");
1668 0 : return false;
1669 :
1670 0 : case PJ_SUCCESS:
1671 0 : port = pj_sockaddr_in_get_port(&mapped_addr);
1672 0 : addr = dhtnet::IpAddr((const sockaddr_in&) mapped_addr).toString(true);
1673 0 : JAMI_DEBUG("STUN server {:s} replied '{}'", sip_utils::as_view(*stunServerName), addr);
1674 0 : return true;
1675 :
1676 0 : default: // use given address, silent any not handled error
1677 0 : JAMI_WARNING("Error from STUN server {:s}, using source address", sip_utils::as_view(*stunServerName));
1678 0 : return false;
1679 : }
1680 : }
1681 : #undef RETURN_IF_NULL
1682 : #undef RETURN_FALSE_IF_NULL
1683 : } // namespace jami
|