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 297 : 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 297 : if (!pjsip_rdata_get_tsx(rdata))
199 297 : 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 409 : is_uninitialized(std::weak_ptr<T> const& weak)
210 : {
211 : using wt = std::weak_ptr<T>;
212 409 : return !weak.owner_before(wt {}) && !wt {}.owner_before(weak);
213 : }
214 :
215 : static pj_bool_t
216 409 : transaction_request_cb(pjsip_rx_data* rdata)
217 : {
218 409 : if (!rdata or !rdata->msg_info.msg) {
219 0 : JAMI_ERR("rx_data is NULL");
220 0 : return PJ_FALSE;
221 : }
222 :
223 409 : pjsip_method* method = &rdata->msg_info.msg->line.req.method;
224 :
225 409 : if (!method) {
226 0 : JAMI_ERR("method is NULL");
227 0 : return PJ_FALSE;
228 : }
229 :
230 409 : if (method->id == PJSIP_ACK_METHOD && pjsip_rdata_get_dlg(rdata))
231 0 : return PJ_FALSE;
232 :
233 409 : 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 409 : pjsip_uri_get_uri(rdata->msg_info.to->uri));
240 : const auto sip_from_uri = reinterpret_cast<pjsip_sip_uri*>(
241 409 : pjsip_uri_get_uri(rdata->msg_info.from->uri));
242 409 : const pjsip_host_port& sip_via = rdata->msg_info.via->sent_by;
243 :
244 409 : 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 409 : std::string_view toUsername(sip_to_uri->user.ptr, sip_to_uri->user.slen);
250 409 : std::string_view toHost(sip_to_uri->host.ptr, sip_to_uri->host.slen);
251 409 : std::string_view viaHostname(sip_via.host.ptr, sip_via.host.slen);
252 409 : const std::string_view remote_user(sip_from_uri->user.ptr, sip_from_uri->user.slen);
253 409 : const std::string_view remote_hostname(sip_from_uri->host.ptr, sip_from_uri->host.slen);
254 409 : std::string peerNumber;
255 409 : if (not remote_user.empty() and not remote_hostname.empty())
256 409 : 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 409 : auto transport = Manager::instance().sipVoIPLink().sipTransportBroker->addTransport(
267 409 : rdata->tp_info.transport);
268 :
269 409 : std::shared_ptr<SIPAccountBase> account;
270 : // If transport account is default-constructed, guessing account is allowed
271 409 : const auto& waccount = transport ? transport->getAccount() : std::weak_ptr<SIPAccountBase> {};
272 409 : 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 394 : } else if (!(account = waccount.lock())) {
286 0 : JAMI_ERR("Dropping SIP request: account is expired.");
287 0 : return PJ_FALSE;
288 : }
289 :
290 409 : pjsip_msg_body* body = rdata->msg_info.msg->body;
291 :
292 409 : if (method->id == PJSIP_OTHER_METHOD) {
293 297 : std::string_view request = sip_utils::as_view(method->name);
294 :
295 297 : 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 297 : } else if (request.find(sip_utils::SIP_METHODS::MESSAGE) != std::string_view::npos) {
320 : // Reply 200 immediately (RFC 3428, ch. 7)
321 297 : try_respond_stateless(endpt_, rdata, PJSIP_SC_OK, nullptr, nullptr, nullptr);
322 : // Process message content in case of multi-part body
323 297 : auto payloads = im::parseSipMessage(rdata->msg_info.msg);
324 297 : if (payloads.size() > 0) {
325 297 : constexpr pj_str_t STR_MESSAGE_ID = jami::sip_utils::CONST_PJ_STR("Message-ID");
326 : auto* msgId = (pjsip_generic_string_hdr*)
327 297 : pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_MESSAGE_ID, nullptr);
328 297 : std::string id = {};
329 297 : 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 297 : if (msgId)
339 297 : id = std::string(msgId->hvalue.ptr, msgId->hvalue.slen);
340 :
341 297 : if (not id.empty()) {
342 : try {
343 : // Mark message as treated
344 297 : auto intid = from_hex_string(id);
345 297 : auto acc = std::dynamic_pointer_cast<JamiAccount>(account);
346 297 : if (acc and acc->isMessageTreated(intid))
347 8 : return PJ_FALSE;
348 297 : } catch (...) {
349 0 : }
350 : }
351 289 : account->onTextMessage(id, peerNumber, std::string(transport->deviceId()), payloads);
352 297 : }
353 289 : return PJ_FALSE;
354 297 : }
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 409 : }
573 :
574 : static void
575 2621 : tp_state_callback(pjsip_transport* tp,
576 : pjsip_transport_state state,
577 : const pjsip_transport_state_info* info)
578 : {
579 2621 : if (auto& broker = Manager::instance().sipVoIPLink().sipTransportBroker)
580 2621 : broker->transportStateChanged(tp, state, info);
581 : else
582 0 : JAMI_ERR("SIPVoIPLink with invalid SipTransportBroker");
583 2621 : }
584 :
585 : /*************************************************************************************************/
586 :
587 : pjsip_endpoint*
588 601 : SIPVoIPLink::getEndpoint()
589 : {
590 601 : 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 1765 : SIPVoIPLink::getCachingPool() noexcept
607 : {
608 1765 : 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 8032 : while (running_)
733 7999 : 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("Shutting down 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 shutdown successfully completed", 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 45 : JAMI_LOG("username = {}, server = {}, from = {}",
771 : userName,
772 : server,
773 : fromUri);
774 : // Attempt to find the account id from username and server name by full match
775 :
776 15 : std::shared_ptr<SIPAccountBase> result;
777 15 : std::shared_ptr<SIPAccountBase> IP2IPAccount;
778 15 : MatchRank best = MatchRank::NONE;
779 :
780 : // SIP accounts
781 42 : for (const auto& account : Manager::instance().getAllAccounts<SIPAccount>()) {
782 33 : const MatchRank match(account->matches(userName, server));
783 :
784 : // return right away if this is a full match
785 33 : if (match == MatchRank::FULL) {
786 6 : return account;
787 27 : } else if (match > best) {
788 9 : best = match;
789 9 : result = account;
790 18 : } else if (!IP2IPAccount && account->isIP2IP()) {
791 : // Allow IP2IP calls if an account exists for this type of calls
792 9 : IP2IPAccount = account;
793 : }
794 15 : }
795 :
796 9 : return result ? result : IP2IPAccount;
797 15 : }
798 :
799 : // Called from EventThread::run (not main thread)
800 : void
801 7999 : SIPVoIPLink::handleEvents()
802 : {
803 7999 : const pj_time_val timeout = {1, 0};
804 7999 : if (auto ret = pjsip_endpt_handle_events(endpt_, &timeout))
805 0 : JAMI_ERR("pjsip_endpt_handle_events failed with error %s",
806 : sip_utils::sip_strerror(ret).c_str());
807 7999 : }
808 :
809 : void
810 0 : SIPVoIPLink::registerKeepAliveTimer(pj_timer_entry& timer, pj_time_val& delay)
811 : {
812 0 : JAMI_DEBUG("Register new keepalive timer {:d} with delay {:d}", timer.id, delay.sec);
813 :
814 0 : if (timer.id == -1)
815 0 : JAMI_WARN("Timer already scheduled");
816 :
817 0 : switch (pjsip_endpt_schedule_timer(endpt_, &timer, &delay)) {
818 0 : case PJ_SUCCESS:
819 0 : break;
820 :
821 0 : default:
822 0 : JAMI_ERR("Unable to schedule new timer in pjsip endpoint");
823 :
824 : /* fallthrough */
825 0 : case PJ_EINVAL:
826 0 : JAMI_ERR("Invalid timer or delay entry");
827 0 : break;
828 :
829 0 : case PJ_EINVALIDOP:
830 0 : JAMI_ERR("Invalid timer entry, maybe already scheduled");
831 0 : break;
832 : }
833 0 : }
834 :
835 : void
836 0 : SIPVoIPLink::cancelKeepAliveTimer(pj_timer_entry& timer)
837 : {
838 0 : pjsip_endpt_cancel_timer(endpt_, &timer);
839 0 : }
840 :
841 : ///////////////////////////////////////////////////////////////////////////////
842 : // Private functions
843 : ///////////////////////////////////////////////////////////////////////////////
844 :
845 : static std::shared_ptr<SIPCall>
846 5519 : getCallFromInvite(pjsip_inv_session* inv)
847 : {
848 5519 : if (auto call_ptr = static_cast<SIPCall*>(inv->mod_data[mod_ua_.id]))
849 4742 : return std::static_pointer_cast<SIPCall>(call_ptr->shared_from_this());
850 777 : return nullptr;
851 : }
852 :
853 : static void
854 1027 : invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* ev)
855 : {
856 1027 : if (inv == nullptr or ev == nullptr) {
857 0 : throw VoipLinkException("Unexpected null pointer");
858 : }
859 :
860 1027 : auto call = getCallFromInvite(inv);
861 1027 : if (not call)
862 104 : return;
863 :
864 923 : if (ev->type != PJSIP_EVENT_TSX_STATE and ev->type != PJSIP_EVENT_TX_MSG
865 96 : and ev->type != PJSIP_EVENT_RX_MSG) {
866 0 : JAMI_WARN("[call:%s] INVITE@%p state changed to %d (%s): unexpected event type %d",
867 : call->getCallId().c_str(),
868 : inv,
869 : inv->state,
870 : pjsip_inv_state_name(inv->state),
871 : ev->type);
872 0 : return;
873 : }
874 :
875 923 : decltype(pjsip_transaction::status_code) status_code = 0;
876 :
877 923 : if (ev->type == PJSIP_EVENT_TSX_STATE) {
878 731 : const auto tsx = ev->body.tsx_state.tsx;
879 731 : status_code = tsx ? tsx->status_code : PJSIP_SC_NOT_FOUND;
880 731 : const pj_str_t* description = pjsip_get_status_text(status_code);
881 :
882 2193 : JAMI_LOG("[call:{}] INVITE@{:p} state changed to {:d} ({}): cause={:d}, tsx@{:p} status {:d} ({:s})",
883 : call->getCallId(),
884 : fmt::ptr(inv),
885 : (int)inv->state,
886 : pjsip_inv_state_name(inv->state),
887 : (int)inv->cause,
888 : fmt::ptr(tsx),
889 : status_code,
890 : sip_utils::as_view(*description));
891 192 : } else if (ev->type == PJSIP_EVENT_TX_MSG) {
892 288 : JAMI_LOG("[call:{}] INVITE@{:p} state changed to {:d} ({:s}): cause={:d} (TX_MSG)",
893 : call->getCallId(),
894 : fmt::ptr(inv),
895 : (int)inv->state,
896 : pjsip_inv_state_name(inv->state),
897 : (int)inv->cause);
898 : }
899 923 : pjsip_rx_data* rdata {nullptr};
900 923 : if (ev->type == PJSIP_EVENT_RX_MSG) {
901 96 : rdata = ev->body.rx_msg.rdata;
902 827 : } else if (ev->type == PJSIP_EVENT_TSX_STATE and ev->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
903 303 : rdata = ev->body.tsx_state.src.rdata;
904 : }
905 923 : if (rdata != nullptr) {
906 399 : call->setPeerUaVersion(sip_utils::getPeerUserAgent(rdata));
907 399 : auto methods = sip_utils::getPeerAllowMethods(rdata);
908 399 : if (not methods.empty()) {
909 208 : call->setPeerAllowMethods(std::move(methods));
910 : }
911 399 : }
912 :
913 923 : switch (inv->state) {
914 214 : case PJSIP_INV_STATE_EARLY:
915 214 : if (status_code == PJSIP_SC_RINGING)
916 214 : call->onPeerRinging();
917 214 : break;
918 :
919 192 : case PJSIP_INV_STATE_CONFIRMED:
920 : // After we sent or received a ACK - The connection is established
921 192 : call->onAnswered();
922 192 : break;
923 :
924 109 : case PJSIP_INV_STATE_DISCONNECTED:
925 109 : switch (inv->cause) {
926 : // When a peer's device replies busy
927 2 : case PJSIP_SC_BUSY_HERE:
928 2 : call->onBusyHere();
929 2 : break;
930 : // When the peer manually refuse the call
931 4 : case PJSIP_SC_DECLINE:
932 : case PJSIP_SC_BUSY_EVERYWHERE:
933 4 : if (inv->role != PJSIP_ROLE_UAC)
934 2 : break;
935 : // close call
936 2 : call->onClosed();
937 2 : break;
938 : // The call terminates normally - BYE / CANCEL
939 101 : case PJSIP_SC_OK:
940 : case PJSIP_SC_REQUEST_TERMINATED:
941 101 : call->onClosed();
942 101 : break;
943 :
944 : // Error/unhandled conditions
945 2 : default:
946 2 : call->onFailure(inv->cause);
947 2 : break;
948 : }
949 109 : break;
950 :
951 408 : default:
952 408 : break;
953 : }
954 1027 : }
955 :
956 : static void
957 28 : on_rx_offer2(pjsip_inv_session* inv, struct pjsip_inv_on_rx_offer_cb_param* param)
958 : {
959 28 : if (not param or not param->offer) {
960 0 : JAMI_ERR("Invalid offer");
961 28 : return;
962 : }
963 :
964 28 : auto call = getCallFromInvite(inv);
965 28 : if (not call)
966 0 : return;
967 :
968 : // This callback is called whenever a new media offer is found in a
969 : // SIP message, typically in a re-invite and in a '200 OK' (as a
970 : // response to an empty invite).
971 : // Here we only handle the second case. The first case is handled
972 : // in reinvite_received_cb.
973 28 : if (inv->cause != PJSIP_SC_OK) {
974 : // Silently ignore if it's not a '200 OK'
975 28 : return;
976 : }
977 :
978 0 : if (auto call = getCallFromInvite(inv)) {
979 0 : if (auto const& account = call->getAccount().lock()) {
980 0 : call->onReceiveOfferIn200OK(param->offer);
981 0 : }
982 0 : }
983 28 : }
984 :
985 : static pj_status_t
986 28 : reinvite_received_cb(pjsip_inv_session* inv, const pjmedia_sdp_session* offer, pjsip_rx_data* rdata)
987 : {
988 28 : if (!offer)
989 0 : return !PJ_SUCCESS;
990 28 : if (auto call = getCallFromInvite(inv)) {
991 28 : if (auto const& account = call->getAccount().lock()) {
992 28 : return call->onReceiveReinvite(offer, rdata);
993 28 : }
994 28 : }
995 :
996 : // Return success if there is no matching call. The re-invite
997 : // should be ignored.
998 0 : return PJ_SUCCESS;
999 : }
1000 :
1001 : static void
1002 0 : sdp_create_offer_cb(pjsip_inv_session* inv, pjmedia_sdp_session** p_offer)
1003 : {
1004 0 : auto call = getCallFromInvite(inv);
1005 0 : if (not call)
1006 0 : return;
1007 :
1008 0 : auto account = call->getSIPAccount();
1009 0 : if (not account) {
1010 0 : JAMI_ERR("No account detected");
1011 0 : return;
1012 : }
1013 :
1014 0 : if (account->isEmptyOffersEnabled()) {
1015 : // Skip if the client wants to send an empty offer.
1016 0 : JAMI_DBG("Client requested to send an empty offer (no SDP)");
1017 0 : return;
1018 : }
1019 :
1020 0 : auto family = pj_AF_INET();
1021 : // FIXME: for now, use the same address family as the SIP transport
1022 0 : if (auto dlg = inv->dlg) {
1023 0 : if (dlg->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
1024 0 : if (auto tr = dlg->tp_sel.u.transport)
1025 0 : family = tr->local_addr.addr.sa_family;
1026 0 : } else if (dlg->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
1027 0 : if (auto tr = dlg->tp_sel.u.listener)
1028 0 : family = tr->local_addr.addr.sa_family;
1029 : }
1030 : }
1031 0 : auto ifaceAddr = dhtnet::ip_utils::getInterfaceAddr(account->getLocalInterface(), family);
1032 :
1033 0 : dhtnet::IpAddr address;
1034 0 : if (account->getUPnPActive()) {
1035 : /* use UPnP addr, or published addr if it's set */
1036 0 : address = account->getPublishedSameasLocal() ? account->getUPnPIpAddress()
1037 0 : : account->getPublishedIpAddress();
1038 : } else {
1039 0 : address = account->getPublishedSameasLocal() ? ifaceAddr : account->getPublishedIpAddress();
1040 : }
1041 :
1042 : /* fallback on local address */
1043 0 : if (not address)
1044 0 : address = ifaceAddr;
1045 :
1046 0 : auto& sdp = call->getSDP();
1047 0 : sdp.setPublishedIP(address);
1048 :
1049 : // This list should be provided by the client. Kept for backward compatibility.
1050 0 : auto const& mediaList = call->getMediaAttributeList();
1051 0 : if (mediaList.empty()) {
1052 0 : throw VoipLinkException("Unexpected empty media attribute list");
1053 : }
1054 :
1055 0 : JAMI_DBG("Creating a SDP offer using the following media:");
1056 0 : for (auto const& media : mediaList) {
1057 0 : JAMI_DBG("[call %s] Media %s", call->getCallId().c_str(), media.toString(true).c_str());
1058 : }
1059 :
1060 0 : const bool created = sdp.createOffer(mediaList);
1061 :
1062 0 : if (created and p_offer != nullptr)
1063 0 : *p_offer = sdp.getLocalSdpSession();
1064 0 : }
1065 :
1066 : static const pjmedia_sdp_session*
1067 247 : get_active_remote_sdp(pjsip_inv_session* inv)
1068 : {
1069 247 : const pjmedia_sdp_session* sdp_session {};
1070 :
1071 247 : if (pjmedia_sdp_neg_get_active_remote(inv->neg, &sdp_session) != PJ_SUCCESS) {
1072 0 : JAMI_ERR("Active remote not present");
1073 0 : return nullptr;
1074 : }
1075 :
1076 247 : if (pjmedia_sdp_validate(sdp_session) != PJ_SUCCESS) {
1077 0 : JAMI_ERR("Invalid remote SDP session");
1078 0 : return nullptr;
1079 : }
1080 :
1081 247 : return sdp_session;
1082 : }
1083 :
1084 : static const pjmedia_sdp_session*
1085 247 : get_active_local_sdp(pjsip_inv_session* inv)
1086 : {
1087 247 : const pjmedia_sdp_session* sdp_session {};
1088 :
1089 247 : if (pjmedia_sdp_neg_get_active_local(inv->neg, &sdp_session) != PJ_SUCCESS) {
1090 0 : JAMI_ERR("Active local not present");
1091 0 : return nullptr;
1092 : }
1093 :
1094 247 : if (pjmedia_sdp_validate(sdp_session) != PJ_SUCCESS) {
1095 0 : JAMI_ERR("Invalid local SDP session");
1096 0 : return nullptr;
1097 : }
1098 :
1099 247 : return sdp_session;
1100 : }
1101 :
1102 : // This callback is called after SDP offer/answer session has completed.
1103 : static void
1104 250 : sdp_media_update_cb(pjsip_inv_session* inv, pj_status_t status)
1105 : {
1106 250 : auto call = getCallFromInvite(inv);
1107 250 : if (not call)
1108 2 : return;
1109 :
1110 248 : JAMI_DBG("[call:%s] INVITE@%p media update: status %d", call->getCallId().c_str(), inv, status);
1111 :
1112 248 : if (status != PJ_SUCCESS) {
1113 2 : const int reason = inv->state != PJSIP_INV_STATE_NULL
1114 1 : and inv->state != PJSIP_INV_STATE_CONFIRMED
1115 2 : ? PJSIP_SC_UNSUPPORTED_MEDIA_TYPE
1116 : : 0;
1117 :
1118 1 : JAMI_WARN("[call:%s] SDP offer failed, reason %d", call->getCallId().c_str(), reason);
1119 :
1120 1 : call->hangup(reason);
1121 1 : return;
1122 : }
1123 :
1124 : // Fetch SDP data from request
1125 247 : const auto localSDP = get_active_local_sdp(inv);
1126 247 : const auto remoteSDP = get_active_remote_sdp(inv);
1127 :
1128 : // Update our SDP manager
1129 247 : auto& sdp = call->getSDP();
1130 247 : sdp.setActiveLocalSdpSession(localSDP);
1131 247 : if (localSDP != nullptr) {
1132 247 : Sdp::printSession(localSDP, "Local active session:", sdp.getSdpDirection());
1133 : }
1134 :
1135 247 : sdp.setActiveRemoteSdpSession(remoteSDP);
1136 247 : if (remoteSDP != nullptr) {
1137 247 : Sdp::printSession(remoteSDP, "Remote active session:", sdp.getSdpDirection());
1138 : }
1139 :
1140 247 : call->onMediaNegotiationComplete();
1141 250 : }
1142 :
1143 : static void
1144 0 : outgoing_request_forked_cb(pjsip_inv_session* /*inv*/, pjsip_event* /*e*/)
1145 0 : {}
1146 :
1147 : static bool
1148 224 : handleMediaControl(SIPCall& call, pjsip_msg_body* body)
1149 : {
1150 : /*
1151 : * Incoming INFO request for media control.
1152 : */
1153 224 : constexpr pj_str_t STR_APPLICATION = CONST_PJ_STR("application");
1154 224 : constexpr pj_str_t STR_MEDIA_CONTROL_XML = CONST_PJ_STR("media_control+xml");
1155 :
1156 224 : if (body->len and pj_stricmp(&body->content_type.type, &STR_APPLICATION) == 0
1157 448 : and pj_stricmp(&body->content_type.subtype, &STR_MEDIA_CONTROL_XML) == 0) {
1158 224 : auto body_msg = std::string_view((char*) body->data, (size_t) body->len);
1159 :
1160 : /* Apply and answer the INFO request */
1161 : static constexpr auto PICT_FAST_UPDATE = "picture_fast_update"sv;
1162 : static constexpr auto STREAM_ID = "stream_id"sv;
1163 : static constexpr auto DEVICE_ORIENTATION = "device_orientation"sv;
1164 : static constexpr auto RECORDING_STATE = "recording_state"sv;
1165 : static constexpr auto MUTE_STATE = "mute_state"sv;
1166 : static constexpr auto VOICE_ACTIVITY = "voice_activity"sv;
1167 :
1168 224 : int streamIdx = -1;
1169 224 : if (body_msg.find(STREAM_ID) != std::string_view::npos) {
1170 : // Note: here we use the index of the RTP stream, not its label!
1171 : // Indeed, both sides will have different labels as they have different call IDs
1172 198 : static const std::regex STREAMID_REGEX("<stream_id>([0-9]+)</stream_id>");
1173 198 : std::svmatch matched_pattern;
1174 198 : std::regex_search(body_msg, matched_pattern, STREAMID_REGEX);
1175 198 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1176 : try {
1177 198 : streamIdx = std::stoi(matched_pattern[1]);
1178 0 : } catch (const std::exception& e) {
1179 0 : JAMI_WARN("Error parsing stream index: %s", e.what());
1180 0 : }
1181 : }
1182 198 : }
1183 :
1184 224 : if (body_msg.find(PICT_FAST_UPDATE) != std::string_view::npos) {
1185 150 : call.sendKeyframe(streamIdx);
1186 224 : return true;
1187 74 : } else if (body_msg.find(DEVICE_ORIENTATION) != std::string_view::npos) {
1188 48 : static const std::regex ORIENTATION_REGEX("device_orientation=([-+]?[0-9]+)");
1189 :
1190 48 : std::svmatch matched_pattern;
1191 48 : std::regex_search(body_msg, matched_pattern, ORIENTATION_REGEX);
1192 :
1193 48 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1194 : try {
1195 48 : int rotation = -std::stoi(matched_pattern[1]);
1196 48 : while (rotation <= -180)
1197 0 : rotation += 360;
1198 48 : while (rotation > 180)
1199 0 : rotation -= 360;
1200 48 : JAMI_WARN("Rotate video %d deg.", rotation);
1201 : #ifdef ENABLE_VIDEO
1202 48 : call.setRotation(streamIdx, rotation);
1203 : #endif
1204 0 : } catch (const std::exception& e) {
1205 0 : JAMI_WARN("Error parsing angle: %s", e.what());
1206 0 : }
1207 48 : return true;
1208 : }
1209 74 : } else if (body_msg.find(RECORDING_STATE) != std::string_view::npos) {
1210 21 : static const std::regex REC_REGEX("recording_state=([0-1])");
1211 21 : std::svmatch matched_pattern;
1212 21 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1213 :
1214 21 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1215 : try {
1216 21 : bool state = std::stoi(matched_pattern[1]);
1217 21 : call.peerRecording(state);
1218 0 : } catch (const std::exception& e) {
1219 0 : JAMI_WARN("Error parsing state remote recording: %s", e.what());
1220 0 : }
1221 21 : return true;
1222 : }
1223 26 : } else if (body_msg.find(MUTE_STATE) != std::string_view::npos) {
1224 5 : static const std::regex REC_REGEX("mute_state=([0-1])");
1225 5 : std::svmatch matched_pattern;
1226 5 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1227 :
1228 5 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1229 : try {
1230 5 : bool state = std::stoi(matched_pattern[1]);
1231 5 : call.peerMuted(state, streamIdx);
1232 0 : } catch (const std::exception& e) {
1233 0 : JAMI_WARN("Error parsing state remote mute: %s", e.what());
1234 0 : }
1235 5 : return true;
1236 : }
1237 5 : } else if (body_msg.find(VOICE_ACTIVITY) != std::string_view::npos) {
1238 0 : static const std::regex REC_REGEX("voice_activity=([0-1])");
1239 0 : std::svmatch matched_pattern;
1240 0 : std::regex_search(body_msg, matched_pattern, REC_REGEX);
1241 :
1242 0 : if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
1243 : try {
1244 0 : bool state = std::stoi(matched_pattern[1]);
1245 0 : call.peerVoice(state);
1246 0 : } catch (const std::exception& e) {
1247 0 : JAMI_WARN("Error parsing state remote voice: %s", e.what());
1248 0 : }
1249 0 : return true;
1250 : }
1251 0 : }
1252 : }
1253 :
1254 0 : return false;
1255 : }
1256 :
1257 : /**
1258 : * Helper function to process refer function on call transfer
1259 : */
1260 : static bool
1261 2 : transferCall(SIPCall& call, const std::string& refer_to)
1262 : {
1263 2 : const auto& callId = call.getCallId();
1264 2 : JAMI_WARN("[call:%s] Attempting to transfer to %s", callId.c_str(), refer_to.c_str());
1265 : try {
1266 4 : Manager::instance().newOutgoingCall(refer_to,
1267 4 : call.getAccountId(),
1268 4 : MediaAttribute::mediaAttributesToMediaMaps(
1269 4 : call.getMediaAttributeList()));
1270 2 : Manager::instance().hangupCall(call.getAccountId(), callId);
1271 0 : } catch (const std::exception& e) {
1272 0 : JAMI_ERR("[call:%s] SIP transfer failed: %s", callId.c_str(), e.what());
1273 0 : return false;
1274 0 : }
1275 2 : return true;
1276 : }
1277 :
1278 : static void
1279 226 : replyToRequest(pjsip_inv_session* inv, pjsip_rx_data* rdata, int status_code)
1280 : {
1281 226 : const auto ret = pjsip_dlg_respond(inv->dlg, rdata, status_code, nullptr, nullptr, nullptr);
1282 226 : if (ret != PJ_SUCCESS)
1283 0 : JAMI_WARN("SIP: Failed to reply %d to request", status_code);
1284 226 : }
1285 :
1286 : static void
1287 2 : onRequestRefer(pjsip_inv_session* inv, pjsip_rx_data* rdata, pjsip_msg* msg, SIPCall& call)
1288 : {
1289 : static constexpr pj_str_t str_refer_to = CONST_PJ_STR("Refer-To");
1290 :
1291 2 : if (auto refer_to = static_cast<pjsip_generic_string_hdr*>(
1292 2 : pjsip_msg_find_hdr_by_name(msg, &str_refer_to, nullptr))) {
1293 : // RFC 3515, 2.4.2: reply bad request if no or too many refer-to header.
1294 4 : if (static_cast<void*>(refer_to->next) == static_cast<void*>(&msg->hdr)
1295 2 : or !pjsip_msg_find_hdr_by_name(msg, &str_refer_to, refer_to->next)) {
1296 2 : replyToRequest(inv, rdata, PJSIP_SC_ACCEPTED);
1297 2 : transferCall(call, std::string(refer_to->hvalue.ptr, refer_to->hvalue.slen));
1298 :
1299 : // RFC 3515, 2.4.4: we MUST handle the processing using NOTIFY msgs
1300 : // But your current design doesn't permit that
1301 2 : return;
1302 : } else
1303 0 : JAMI_ERR("[call:%s] REFER: too many Refer-To headers", call.getCallId().c_str());
1304 : } else
1305 0 : JAMI_ERR("[call:%s] REFER: no Refer-To header", call.getCallId().c_str());
1306 :
1307 0 : replyToRequest(inv, rdata, PJSIP_SC_BAD_REQUEST);
1308 : }
1309 :
1310 : static void
1311 224 : onRequestInfo(pjsip_inv_session* inv, pjsip_rx_data* rdata, pjsip_msg* msg, SIPCall& call)
1312 : {
1313 224 : if (!msg->body or handleMediaControl(call, msg->body))
1314 224 : replyToRequest(inv, rdata, PJSIP_SC_OK);
1315 224 : }
1316 :
1317 : static void
1318 0 : onRequestNotify(pjsip_inv_session* /*inv*/, pjsip_rx_data* /*rdata*/, pjsip_msg* msg, SIPCall& call)
1319 : {
1320 0 : if (!msg->body)
1321 0 : return;
1322 :
1323 0 : const std::string bodyText {static_cast<char*>(msg->body->data), msg->body->len};
1324 0 : JAMI_DBG("[call:%s] NOTIFY body start - %p\n%s\n[call:%s] NOTIFY body end - %p",
1325 : call.getCallId().c_str(),
1326 : msg->body,
1327 : bodyText.c_str(),
1328 : call.getCallId().c_str(),
1329 : msg->body);
1330 :
1331 : // TODO
1332 0 : }
1333 :
1334 : static void
1335 4187 : transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjsip_event* event)
1336 : {
1337 4187 : auto call = getCallFromInvite(inv);
1338 4183 : if (not call)
1339 672 : return;
1340 :
1341 : #ifdef DEBUG_SIP_REQUEST_MSG
1342 : processInviteResponseHelper(inv, event);
1343 : #endif
1344 :
1345 : // We process here only incoming request message
1346 3511 : if (tsx->role != PJSIP_ROLE_UAS or tsx->state != PJSIP_TSX_STATE_TRYING
1347 457 : or event->body.tsx_state.type != PJSIP_EVENT_RX_MSG) {
1348 3054 : return;
1349 : }
1350 :
1351 457 : const auto rdata = event->body.tsx_state.src.rdata;
1352 457 : if (!rdata) {
1353 0 : JAMI_ERROR("[INVITE:{:p}] SIP RX request without rx data", fmt::ptr(inv));
1354 0 : return;
1355 : }
1356 :
1357 457 : const auto msg = rdata->msg_info.msg;
1358 457 : if (msg->type != PJSIP_REQUEST_MSG) {
1359 0 : JAMI_ERROR("[INVITE:{:p}] SIP RX request without msg", fmt::ptr(inv));
1360 0 : return;
1361 : }
1362 :
1363 : // Using method name to dispatch
1364 457 : auto methodName = sip_utils::as_view(msg->line.req.method.name);
1365 1375 : JAMI_LOG("[INVITE:{:p}] RX SIP method {:d} ({:s})",
1366 : fmt::ptr(inv),
1367 : (int)msg->line.req.method.id,
1368 : methodName);
1369 :
1370 : #ifdef DEBUG_SIP_REQUEST_MSG
1371 : char msgbuf[1000];
1372 : auto msgsize = pjsip_msg_print(msg, msgbuf, sizeof msgbuf);
1373 : if (msgsize > 0)
1374 : JAMI_LOG("{:s}", std::string_view(msgbuf, msgsize));
1375 : #endif // DEBUG_SIP_REQUEST_MSG
1376 :
1377 459 : if (methodName == sip_utils::SIP_METHODS::REFER)
1378 2 : onRequestRefer(inv, rdata, msg, *call);
1379 457 : else if (methodName == sip_utils::SIP_METHODS::INFO)
1380 224 : onRequestInfo(inv, rdata, msg, *call);
1381 233 : else if (methodName == sip_utils::SIP_METHODS::NOTIFY)
1382 0 : onRequestNotify(inv, rdata, msg, *call);
1383 233 : else if (methodName == sip_utils::SIP_METHODS::OPTIONS)
1384 0 : handleIncomingOptions(rdata);
1385 233 : else if (methodName == sip_utils::SIP_METHODS::MESSAGE) {
1386 199 : if (msg->body)
1387 199 : runOnMainThread([call, m = im::parseSipMessage(msg)]() mutable {
1388 199 : call->onTextMessage(std::move(m));
1389 199 : });
1390 : }
1391 4185 : }
1392 :
1393 : #ifdef DEBUG_SIP_REQUEST_MSG
1394 : static void
1395 : processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* event)
1396 : {
1397 : if (event->body.tsx_state.type != PJSIP_EVENT_RX_MSG)
1398 : return;
1399 :
1400 : const auto rdata = event->body.tsx_state.src.rdata;
1401 : if (rdata == nullptr or rdata->msg_info.msg == nullptr)
1402 : return;
1403 :
1404 : const auto msg = rdata->msg_info.msg;
1405 : if (msg->type != PJSIP_RESPONSE_MSG)
1406 : return;
1407 :
1408 : // Only handle the following responses
1409 : switch (msg->line.status.code) {
1410 : case PJSIP_SC_TRYING:
1411 : case PJSIP_SC_RINGING:
1412 : case PJSIP_SC_OK:
1413 : break;
1414 : default:
1415 : return;
1416 : }
1417 :
1418 : JAMI_LOG("[INVITE:{:p}] SIP RX response: reason {:s}, status code {:d} {:s}",
1419 : fmt::ptr(inv),
1420 : sip_utils::as_view(msg->line.status.reason),
1421 : msg->line.status.code,
1422 : sip_utils::as_view(*pjsip_get_status_text(msg->line.status.code)));
1423 :
1424 : sip_utils::logMessageHeaders(&msg->hdr);
1425 : }
1426 : #endif
1427 :
1428 : int
1429 630 : SIPVoIPLink::getModId()
1430 : {
1431 630 : return mod_ua_.id;
1432 : }
1433 :
1434 : void
1435 0 : SIPVoIPLink::createSDPOffer(pjsip_inv_session* inv)
1436 : {
1437 0 : if (inv == nullptr) {
1438 0 : throw VoipLinkException("Invite session is unable to be null");
1439 : }
1440 0 : sdp_create_offer_cb(inv, nullptr);
1441 0 : }
1442 :
1443 : // Thread-safe DNS resolver callback mapping
1444 : class SafeResolveCallbackMap
1445 : {
1446 : public:
1447 : using ResolveCallback = std::function<void(pj_status_t, const pjsip_server_addresses*)>;
1448 :
1449 5 : void registerCallback(uintptr_t key, ResolveCallback&& cb)
1450 : {
1451 5 : std::lock_guard lk(mutex_);
1452 5 : cbMap_.emplace(key, std::move(cb));
1453 5 : }
1454 :
1455 5 : void process(uintptr_t key, pj_status_t status, const pjsip_server_addresses* addr)
1456 : {
1457 5 : std::lock_guard lk(mutex_);
1458 5 : auto it = cbMap_.find(key);
1459 5 : if (it != cbMap_.end()) {
1460 5 : it->second(status, addr);
1461 5 : cbMap_.erase(it);
1462 : }
1463 5 : }
1464 :
1465 : private:
1466 : std::mutex mutex_;
1467 : std::map<uintptr_t, ResolveCallback> cbMap_;
1468 : };
1469 :
1470 : static SafeResolveCallbackMap&
1471 10 : getResolveCallbackMap()
1472 : {
1473 10 : static SafeResolveCallbackMap map;
1474 10 : return map;
1475 : }
1476 :
1477 : static void
1478 5 : resolver_callback(pj_status_t status, void* token, const struct pjsip_server_addresses* addr)
1479 : {
1480 5 : getResolveCallbackMap().process((uintptr_t) token, status, addr);
1481 5 : }
1482 :
1483 : void
1484 5 : SIPVoIPLink::resolveSrvName(const std::string& name,
1485 : pjsip_transport_type_e type,
1486 : SrvResolveCallback&& cb)
1487 : {
1488 : // PJSIP limits hostname to be longer than PJ_MAX_HOSTNAME.
1489 : // But, resolver prefix the given name by a string like "_sip._udp."
1490 : // causing a check against PJ_MAX_HOSTNAME to be useless.
1491 : // It's not easy to pre-determinate as it's implementation dependent.
1492 : // So we just choose a security marge enough for most cases, preventing a crash later
1493 : // in the call of pjsip_endpt_resolve().
1494 5 : if (name.length() > (PJ_MAX_HOSTNAME - 12)) {
1495 0 : JAMI_ERR("Hostname is too long");
1496 0 : cb({});
1497 0 : return;
1498 : }
1499 :
1500 : // extract port if name is in form "server:port"
1501 : int port;
1502 : pj_ssize_t name_size;
1503 5 : const auto n = name.rfind(':');
1504 5 : if (n != std::string::npos) {
1505 0 : port = std::atoi(name.c_str() + n + 1);
1506 0 : name_size = n;
1507 : } else {
1508 5 : port = 0;
1509 5 : name_size = name.size();
1510 : }
1511 5 : JAMI_DBG("Attempt to resolve '%s' (port: %u)", name.c_str(), port);
1512 :
1513 5 : pjsip_host_info host_info {
1514 : /*.flag = */ 0,
1515 : /*.type = */ type,
1516 5 : /*.addr = */ {{(char*) name.c_str(), name_size}, port},
1517 5 : };
1518 :
1519 5 : const auto token = std::hash<std::string>()(name + std::to_string(type));
1520 10 : getResolveCallbackMap().registerCallback(
1521 5 : token, [=, cb = std::move(cb)](pj_status_t s, const pjsip_server_addresses* r) {
1522 : try {
1523 5 : if (s != PJ_SUCCESS || !r) {
1524 0 : JAMI_WARN("Unable to resolve \"%s\" using pjsip_endpt_resolve, attempting getaddrinfo.",
1525 : name.c_str());
1526 0 : dht::ThreadPool::io().run([=, cb = std::move(cb)]() {
1527 0 : auto ips = dhtnet::ip_utils::getAddrList(name.c_str());
1528 0 : runOnMainThread(std::bind(cb, std::move(ips)));
1529 0 : });
1530 0 : } else {
1531 5 : std::vector<dhtnet::IpAddr> ips;
1532 5 : ips.reserve(r->count);
1533 10 : for (unsigned i = 0; i < r->count; i++)
1534 5 : ips.push_back(r->entry[i].addr);
1535 5 : cb(ips);
1536 5 : }
1537 0 : } catch (const std::exception& e) {
1538 0 : JAMI_ERR("Error resolving address: %s", e.what());
1539 0 : cb({});
1540 0 : }
1541 5 : });
1542 :
1543 5 : pjsip_endpt_resolve(endpt_, pool_.get(), &host_info, (void*) token, resolver_callback);
1544 : }
1545 :
1546 : #define RETURN_IF_NULL(A, ...) \
1547 : if ((A) == NULL) { \
1548 : JAMI_WARN(__VA_ARGS__); \
1549 : return; \
1550 : }
1551 :
1552 : #define RETURN_FALSE_IF_NULL(A, ...) \
1553 : if ((A) == NULL) { \
1554 : JAMI_WARN(__VA_ARGS__); \
1555 : return false; \
1556 : }
1557 :
1558 : void
1559 27 : SIPVoIPLink::findLocalAddressFromTransport(pjsip_transport* transport,
1560 : pjsip_transport_type_e transportType,
1561 : const std::string& host,
1562 : std::string& addr,
1563 : pj_uint16_t& port) const
1564 : {
1565 : // Initialize the SIP port with the default SIP port
1566 27 : port = pjsip_transport_get_default_port_for_type(transportType);
1567 :
1568 : // Initialize the SIP address with the hostname
1569 27 : addr = sip_utils::as_view(*pj_gethostname());
1570 :
1571 : // Update address and port with active transport
1572 27 : RETURN_IF_NULL(transport,
1573 : "Transport is NULL in findLocalAddress, using local address %s :%d",
1574 : addr.c_str(),
1575 : port);
1576 :
1577 : // get the transport manager associated with the SIP enpoint
1578 27 : auto tpmgr = pjsip_endpt_get_tpmgr(endpt_);
1579 27 : RETURN_IF_NULL(tpmgr,
1580 : "Transport manager is NULL in findLocalAddress, using local address %s :%d",
1581 : addr.c_str(),
1582 : port);
1583 :
1584 27 : const pj_str_t pjstring(CONST_PJ_STR(host));
1585 :
1586 27 : auto tp_sel = getTransportSelector(transport);
1587 27 : pjsip_tpmgr_fla2_param param
1588 27 : = {transportType, &tp_sel, pjstring, PJ_FALSE, {nullptr, 0}, 0, nullptr};
1589 27 : if (pjsip_tpmgr_find_local_addr2(tpmgr, pool_.get(), ¶m) != PJ_SUCCESS) {
1590 0 : JAMI_WARN("Unable to retrieve local address and port from transport, using %s :%d",
1591 : addr.c_str(),
1592 : port);
1593 0 : return;
1594 : }
1595 :
1596 : // Update local address based on the transport type
1597 27 : addr = sip_utils::as_view(param.ret_addr);
1598 :
1599 : // Determine the local port based on transport information
1600 27 : port = param.ret_port;
1601 : }
1602 :
1603 : bool
1604 0 : SIPVoIPLink::findLocalAddressFromSTUN(pjsip_transport* transport,
1605 : pj_str_t* stunServerName,
1606 : int stunPort,
1607 : std::string& addr,
1608 : pj_uint16_t& port) const
1609 : {
1610 : // WARN: this code use pjstun_get_mapped_addr2 that works
1611 : // in IPv4 only.
1612 : // WARN: this function is blocking (network request).
1613 :
1614 : // Initialize the sip port with the default SIP port
1615 0 : port = sip_utils::DEFAULT_SIP_PORT;
1616 :
1617 : // Get Local IP address
1618 0 : auto localIp = dhtnet::ip_utils::getLocalAddr(pj_AF_INET());
1619 0 : if (not localIp) {
1620 0 : JAMI_WARN("Failed to find local IP");
1621 0 : return false;
1622 : }
1623 :
1624 0 : addr = localIp.toString();
1625 :
1626 : // Update address and port with active transport
1627 0 : RETURN_FALSE_IF_NULL(transport,
1628 : "Transport is NULL in findLocalAddress, using local address %s:%u",
1629 : addr.c_str(),
1630 : port);
1631 :
1632 0 : JAMI_DBG("STUN mapping of '%s:%u'", addr.c_str(), port);
1633 :
1634 : pj_sockaddr_in mapped_addr;
1635 0 : pj_sock_t sipSocket = pjsip_udp_transport_get_socket(transport);
1636 0 : const pjstun_setting stunOpt
1637 0 : = {PJ_TRUE, localIp.getFamily(), *stunServerName, stunPort, *stunServerName, stunPort};
1638 0 : pj_status_t stunStatus = pjstun_get_mapped_addr2(&cp_.factory,
1639 : &stunOpt,
1640 : 1,
1641 : &sipSocket,
1642 : &mapped_addr);
1643 :
1644 0 : switch (stunStatus) {
1645 0 : case PJLIB_UTIL_ESTUNNOTRESPOND:
1646 0 : JAMI_ERROR("No response from STUN server {:s}",
1647 : sip_utils::as_view(*stunServerName));
1648 0 : return false;
1649 :
1650 0 : case PJLIB_UTIL_ESTUNSYMMETRIC:
1651 0 : JAMI_ERR("Different mapped addresses are returned by servers.");
1652 0 : return false;
1653 :
1654 0 : case PJ_SUCCESS:
1655 0 : port = pj_sockaddr_in_get_port(&mapped_addr);
1656 0 : addr = dhtnet::IpAddr((const sockaddr_in&) mapped_addr).toString(true);
1657 0 : JAMI_DEBUG("STUN server {:s} replied '{}'", sip_utils::as_view(*stunServerName), addr);
1658 0 : return true;
1659 :
1660 0 : default: // use given address, silent any not handled error
1661 0 : JAMI_WARNING("Error from STUN server {:s}, using source address",
1662 : sip_utils::as_view(*stunServerName));
1663 0 : return false;
1664 : }
1665 : }
1666 : #undef RETURN_IF_NULL
1667 : #undef RETURN_FALSE_IF_NULL
1668 : } // namespace jami
|