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