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