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