Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 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/sipaccount.h"
19 :
20 : #ifdef HAVE_CONFIG_H
21 : #include "config.h"
22 : #endif
23 :
24 : #include "compiler_intrinsics.h"
25 :
26 : #include "vcard.h"
27 : #include "base64.h"
28 : #include "fileutils.h"
29 :
30 : #include "sdp.h"
31 : #include "sip/sipvoiplink.h"
32 : #include "sip/sipcall.h"
33 : #include "connectivity/sip_utils.h"
34 :
35 : #include "call_factory.h"
36 :
37 : #include "sip/sippresence.h"
38 :
39 : #pragma GCC diagnostic push
40 : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
41 : #include <yaml-cpp/yaml.h>
42 : #pragma GCC diagnostic pop
43 :
44 : #include "account_schema.h"
45 : #include "config/yamlparser.h"
46 : #include "logger.h"
47 : #include "manager.h"
48 : #include "client/ring_signal.h"
49 : #include "jami/account_const.h"
50 :
51 : #ifdef ENABLE_VIDEO
52 : #include "libav_utils.h"
53 : #endif
54 :
55 : #include "system_codec_container.h"
56 :
57 : #include "string_utils.h"
58 :
59 : #include "im/instant_messaging.h"
60 :
61 : #include <dhtnet/ip_utils.h>
62 : #include <dhtnet/upnp/upnp_control.h>
63 :
64 : #include <opendht/crypto.h>
65 :
66 : #include <unistd.h>
67 :
68 : #include <algorithm>
69 : #include <array>
70 : #include <memory>
71 : #include <sstream>
72 : #include <cstdlib>
73 : #include <thread>
74 : #include <chrono>
75 : #include <ctime>
76 : #include <charconv>
77 :
78 : #ifdef _WIN32
79 : #include <lmcons.h>
80 : #else
81 : #include <pwd.h>
82 : #endif
83 :
84 : namespace jami {
85 :
86 : using sip_utils::CONST_PJ_STR;
87 :
88 : static constexpr unsigned REGISTRATION_FIRST_RETRY_INTERVAL = 60; // seconds
89 : static constexpr unsigned REGISTRATION_RETRY_INTERVAL = 300; // seconds
90 : static constexpr std::string_view VALID_TLS_PROTOS[] = {"Default"sv, "TLSv1.2"sv, "TLSv1.1"sv, "TLSv1"sv};
91 : static constexpr std::string_view PN_FCM = "fcm"sv;
92 : static constexpr std::string_view PN_APNS = "apns"sv;
93 :
94 : struct ctx
95 : {
96 0 : ctx(pjsip_auth_clt_sess* auth)
97 0 : : auth_sess(auth, &pjsip_auth_clt_deinit)
98 0 : {}
99 : std::weak_ptr<SIPAccount> acc;
100 : std::string to;
101 : uint64_t id;
102 : std::unique_ptr<pjsip_auth_clt_sess, decltype(&pjsip_auth_clt_deinit)> auth_sess;
103 : };
104 :
105 : static void
106 3 : registration_cb(pjsip_regc_cbparam* param)
107 : {
108 3 : if (!param) {
109 0 : JAMI_ERR("Registration callback parameter is null");
110 0 : return;
111 : }
112 :
113 3 : auto account = static_cast<SIPAccount*>(param->token);
114 3 : if (!account) {
115 0 : JAMI_ERR("Account doesn't exist in registration callback");
116 0 : return;
117 : }
118 :
119 3 : account->onRegister(param);
120 : }
121 :
122 24 : SIPAccount::SIPAccount(const std::string& accountID, bool presenceEnabled)
123 : : SIPAccountBase(accountID)
124 24 : , ciphers_(100)
125 48 : , presence_(presenceEnabled ? new SIPPresence(this) : nullptr)
126 : {
127 24 : via_addr_.host.ptr = 0;
128 24 : via_addr_.host.slen = 0;
129 24 : via_addr_.port = 0;
130 24 : }
131 :
132 24 : SIPAccount::~SIPAccount() noexcept
133 : {
134 : // ensure that no registration callbacks survive past this point
135 : try {
136 24 : destroyRegistrationInfo();
137 24 : setTransport();
138 0 : } catch (...) {
139 0 : JAMI_ERR("Exception in SIPAccount destructor");
140 0 : }
141 :
142 24 : delete presence_;
143 24 : }
144 :
145 : void
146 0 : SIPAccount::updateProfile(const std::string& displayName,
147 : const std::string& avatar,
148 : const std::string& fileType,
149 : int32_t flag)
150 : {
151 0 : auto vCardPath = idPath_ / "profile.vcf";
152 :
153 0 : auto profile = getProfileVcard();
154 0 : if (profile.empty()) {
155 0 : profile = vCard::utils::initVcard();
156 : }
157 0 : profile["FN"] = displayName;
158 :
159 0 : if (!fileType.empty()) {
160 0 : const std::string& key = "PHOTO;ENCODING=BASE64;TYPE=" + fileType;
161 0 : if (flag == 0) {
162 0 : vCard::utils::removeByKey(profile, "PHOTO");
163 0 : const auto& avatarPath = std::filesystem::path(avatar);
164 0 : if (std::filesystem::exists(avatarPath)) {
165 : try {
166 0 : profile[key] = base64::encode(fileutils::loadFile(avatarPath));
167 0 : } catch (const std::exception& e) {
168 0 : JAMI_ERROR("Failed to load avatar: {}", e.what());
169 0 : }
170 0 : } else if (avatarPath.empty()) {
171 0 : vCard::utils::removeByKey(profile, "PHOTO");
172 0 : profile[key] = "";
173 : }
174 0 : } else if (flag == 1) {
175 0 : profile[key] = avatar;
176 : }
177 0 : }
178 :
179 : // nothing happens to the profile photo if the avatarPath is invalid
180 : // and not empty. So far it seems to be the best default behavior.
181 : try {
182 0 : auto tmpPath = vCardPath;
183 0 : tmpPath += ".tmp";
184 0 : std::ofstream file(tmpPath);
185 0 : if (file.is_open()) {
186 0 : file << vCard::utils::toString(profile);
187 0 : file.close();
188 0 : std::filesystem::rename(tmpPath, vCardPath);
189 0 : emitSignal<libjami::ConfigurationSignal::ProfileReceived>(getAccountID(), "", vCardPath.string());
190 : } else {
191 0 : JAMI_ERROR("Unable to open file for writing: {}", tmpPath);
192 : }
193 0 : } catch (const std::exception& e) {
194 0 : JAMI_ERROR("Error writing profile: {}", e.what());
195 0 : }
196 0 : }
197 :
198 : std::shared_ptr<SIPCall>
199 10 : SIPAccount::newIncomingCall(const std::string& from UNUSED,
200 : const std::vector<libjami::MediaMap>& mediaList,
201 : const std::shared_ptr<SipTransport>& transport)
202 : {
203 20 : auto call = Manager::instance().callFactory.newSipCall(shared(), Call::CallType::INCOMING, mediaList);
204 10 : call->setSipTransport(transport, getContactHeader());
205 10 : return call;
206 0 : }
207 :
208 : std::shared_ptr<Call>
209 10 : SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<libjami::MediaMap>& mediaList)
210 : {
211 10 : std::string to;
212 : int family;
213 :
214 10 : JAMI_DBG() << *this << "Calling SIP peer" << toUrl;
215 :
216 10 : auto& manager = Manager::instance();
217 10 : std::shared_ptr<SIPCall> call;
218 :
219 : // SIP allows sending empty invites.
220 10 : if (not mediaList.empty() or isEmptyOffersEnabled()) {
221 10 : call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
222 : } else {
223 0 : JAMI_WARN("Media list is empty, setting a default list");
224 0 : call = manager.callFactory.newSipCall(shared(),
225 : Call::CallType::OUTGOING,
226 0 : MediaAttribute::mediaAttributesToMediaMaps(
227 0 : createDefaultMediaList(isVideoEnabled())));
228 : }
229 :
230 10 : if (not call)
231 0 : throw std::runtime_error("Failed to create the call");
232 :
233 10 : if (isIP2IP()) {
234 9 : bool ipv6 = dhtnet::IpAddr::isIpv6(toUrl);
235 9 : to = ipv6 ? dhtnet::IpAddr(toUrl).toString(false, true) : toUrl;
236 9 : family = ipv6 ? pj_AF_INET6() : pj_AF_INET();
237 :
238 : // TODO: resolve remote host using SIPVoIPLink::resolveSrvName
239 : std::shared_ptr<SipTransport> t
240 9 : = isTlsEnabled() ? link_.sipTransportBroker->getTlsTransport(tlsListener_,
241 0 : dhtnet::IpAddr(sip_utils::getHostFromUri(to)))
242 9 : : transport_;
243 9 : setTransport(t);
244 9 : call->setSipTransport(t, getContactHeader());
245 :
246 9 : JAMI_DBG("New %s IP to IP call to %s", ipv6 ? "IPv6" : "IPv4", to.c_str());
247 9 : } else {
248 1 : to = toUrl;
249 1 : call->setSipTransport(transport_, getContactHeader());
250 : // Use the same address family as the SIP transport
251 1 : family = pjsip_transport_type_get_af(getTransportType());
252 :
253 4 : JAMI_LOG("UserAgent: New registered account call to {}", toUrl);
254 : }
255 :
256 10 : auto toUri = getToUri(to);
257 :
258 : // Do not init ICE yet if the media list is empty. This may occur
259 : // if we are sending an invite with no SDP offer.
260 10 : if (call->isIceEnabled() and not mediaList.empty()) {
261 8 : if (call->createIceMediaTransport(false)) {
262 8 : call->initIceMediaTransport(true);
263 : }
264 : }
265 :
266 10 : call->setPeerNumber(toUri);
267 10 : call->setPeerUri(toUri);
268 :
269 10 : const auto localAddress = dhtnet::ip_utils::getInterfaceAddr(getLocalInterface(), family);
270 :
271 10 : dhtnet::IpAddr addrSdp;
272 10 : if (getUPnPActive()) {
273 : /* use UPnP addr, or published addr if its set */
274 0 : addrSdp = getPublishedSameasLocal() ? getUPnPIpAddress() : getPublishedIpAddress();
275 : } else {
276 10 : addrSdp = isStunEnabled() or (not getPublishedSameasLocal()) ? getPublishedIpAddress() : localAddress;
277 : }
278 :
279 : /* Fallback on local address */
280 10 : if (not addrSdp)
281 0 : addrSdp = localAddress;
282 :
283 : // Building the local SDP offer
284 10 : auto& sdp = call->getSDP();
285 :
286 10 : if (getPublishedSameasLocal())
287 10 : sdp.setPublishedIP(addrSdp);
288 : else
289 0 : sdp.setPublishedIP(getPublishedAddress());
290 :
291 : // TODO. We should not dot his here. Move it to SIPCall.
292 10 : const bool created = sdp.createOffer(MediaAttribute::buildMediaAttributesList(mediaList, isSrtpEnabled()));
293 :
294 10 : if (created) {
295 10 : std::weak_ptr<SIPCall> weak_call = call;
296 10 : manager.scheduler().run([this, weak_call] {
297 10 : if (auto call = weak_call.lock()) {
298 10 : if (not SIPStartCall(call)) {
299 0 : JAMI_ERR("Unable to send outgoing INVITE request for new call");
300 0 : call->onFailure();
301 : }
302 10 : }
303 10 : return false;
304 : });
305 10 : } else {
306 0 : throw VoipLinkException("Unable to send outgoing INVITE request for new call");
307 : }
308 :
309 20 : return call;
310 10 : }
311 :
312 : void
313 0 : SIPAccount::onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info* info)
314 : {
315 0 : pj_status_t currentStatus = transportStatus_;
316 0 : JAMI_DEBUG("Transport state changed to {:s} for account {:s}!", SipTransport::stateToStr(state), accountID_);
317 0 : if (!SipTransport::isAlive(state)) {
318 0 : if (info) {
319 0 : transportStatus_ = info->status;
320 0 : transportError_ = sip_utils::sip_strerror(info->status);
321 0 : JAMI_ERROR("Transport disconnected: {:s}", transportError_);
322 : } else {
323 : // This is already the generic error used by PJSIP.
324 0 : transportStatus_ = PJSIP_SC_SERVICE_UNAVAILABLE;
325 0 : transportError_ = "";
326 : }
327 0 : setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_TSX_TRANSPORT_ERROR);
328 0 : setTransport();
329 : } else {
330 : // The status can be '0', this is the same as OK
331 0 : transportStatus_ = info && info->status ? info->status : PJSIP_SC_OK;
332 0 : transportError_ = "";
333 : }
334 :
335 : // Notify the client of the new transport state
336 0 : if (currentStatus != transportStatus_)
337 0 : emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(accountID_, getVolatileAccountDetails());
338 0 : }
339 :
340 : void
341 86 : SIPAccount::setTransport(const std::shared_ptr<SipTransport>& t)
342 : {
343 86 : if (t == transport_)
344 34 : return;
345 52 : if (transport_) {
346 100 : JAMI_DEBUG("Removing old transport [{}] from account", fmt::ptr(transport_.get()));
347 : // NOTE: do not call destroyRegistrationInfo() there as we must call the registration
348 : // callback if needed
349 25 : if (regc_)
350 3 : pjsip_regc_release_transport(regc_);
351 25 : transport_->removeStateListener(reinterpret_cast<uintptr_t>(this));
352 : }
353 :
354 52 : transport_ = t;
355 208 : JAMI_DEBUG("Set new transport [{}]", fmt::ptr(transport_.get()));
356 :
357 52 : if (transport_) {
358 54 : transport_->addStateListener(reinterpret_cast<uintptr_t>(this),
359 0 : std::bind(&SIPAccount::onTransportStateChanged,
360 27 : this,
361 : std::placeholders::_1,
362 : std::placeholders::_2));
363 : // Update contact address and header
364 27 : if (not initContactAddress()) {
365 0 : JAMI_DEBUG("Unable to register: invalid address");
366 0 : return;
367 : }
368 27 : updateContactHeader();
369 : }
370 : }
371 :
372 : pjsip_tpselector
373 8 : SIPAccount::getTransportSelector()
374 : {
375 8 : if (!transport_)
376 0 : return SIPVoIPLink::getTransportSelector(nullptr);
377 8 : return SIPVoIPLink::getTransportSelector(transport_->get());
378 : }
379 :
380 : bool
381 10 : SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
382 : {
383 : // Add Ice headers to local SDP if ice transport exist
384 10 : call->addLocalIceAttributes();
385 :
386 10 : const std::string& toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
387 10 : pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
388 :
389 : // Create the from header
390 10 : std::string from(getFromUri());
391 10 : pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
392 :
393 10 : auto transport = call->getTransport();
394 10 : if (!transport) {
395 0 : JAMI_ERROR("Unable to start call without transport");
396 0 : return false;
397 : }
398 :
399 10 : std::string contact = getContactHeader();
400 40 : JAMI_DEBUG("Contact header: {:s} / {:s} → {:s}", contact, from, toUri);
401 :
402 10 : pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
403 10 : auto local_sdp = isEmptyOffersEnabled() ? nullptr : call->getSDP().getLocalSdpSession();
404 :
405 10 : pjsip_dialog* dialog {nullptr};
406 10 : pjsip_inv_session* inv {nullptr};
407 10 : if (!CreateClientDialogAndInvite(&pjFrom, &pjContact, &pjTo, nullptr, local_sdp, &dialog, &inv))
408 0 : return false;
409 :
410 10 : inv->mod_data[link_.getModId()] = call.get();
411 10 : call->setInviteSession(inv);
412 :
413 10 : updateDialogViaSentBy(dialog);
414 :
415 10 : if (hasServiceRoute())
416 0 : pjsip_dlg_set_route_set(dialog, sip_utils::createRouteSet(getServiceRoute(), call->inviteSession_->pool));
417 :
418 10 : if (hasCredentials()
419 10 : and pjsip_auth_clt_set_credentials(&dialog->auth_sess, getCredentialCount(), getCredInfo()) != PJ_SUCCESS) {
420 0 : JAMI_ERROR("Unable to initialize credentials for invite session authentication");
421 0 : return false;
422 : }
423 :
424 : pjsip_tx_data* tdata;
425 :
426 10 : if (pjsip_inv_invite(call->inviteSession_.get(), &tdata) != PJ_SUCCESS) {
427 0 : JAMI_ERROR("Unable to initialize invite messager for this call");
428 0 : return false;
429 : }
430 :
431 10 : const pjsip_tpselector tp_sel = link_.getTransportSelector(transport->get());
432 10 : if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
433 0 : JAMI_ERROR("Unable to associate transport for invite session dialog");
434 0 : return false;
435 : }
436 :
437 : // Add user-agent header
438 10 : sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
439 :
440 10 : if (pjsip_inv_send_msg(call->inviteSession_.get(), tdata) != PJ_SUCCESS) {
441 0 : JAMI_ERROR("Unable to send invite message for this call");
442 0 : return false;
443 : }
444 :
445 10 : call->setState(Call::CallState::ACTIVE, Call::ConnectionState::PROGRESSING);
446 :
447 10 : return true;
448 10 : }
449 :
450 : void
451 0 : SIPAccount::usePublishedAddressPortInVIA()
452 : {
453 0 : publishedIpStr_ = getPublishedIpAddress().toString();
454 0 : via_addr_.host.ptr = (char*) publishedIpStr_.c_str();
455 0 : via_addr_.host.slen = publishedIpStr_.size();
456 0 : via_addr_.port = publishedPortUsed_;
457 0 : }
458 :
459 : void
460 0 : SIPAccount::useUPnPAddressPortInVIA()
461 : {
462 0 : upnpIpAddr_ = getUPnPIpAddress().toString();
463 0 : via_addr_.host.ptr = (char*) upnpIpAddr_.c_str();
464 0 : via_addr_.host.slen = upnpIpAddr_.size();
465 0 : via_addr_.port = publishedPortUsed_;
466 0 : }
467 :
468 : template<typename T>
469 : static void
470 : validate(std::string& member, const std::string& param, const T& valid)
471 : {
472 : const auto begin = std::begin(valid);
473 : const auto end = std::end(valid);
474 : if (find(begin, end, param) != end)
475 : member = param;
476 : else
477 : JAMI_ERROR("Invalid parameter \"{:s}\"", param);
478 : }
479 :
480 : std::map<std::string, std::string>
481 54 : SIPAccount::getVolatileAccountDetails() const
482 : {
483 54 : auto a = SIPAccountBase::getVolatileAccountDetails();
484 54 : a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, std::to_string(registrationStateDetailed_.first));
485 54 : a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_DESC, registrationStateDetailed_.second);
486 54 : a.emplace(libjami::Account::VolatileProperties::InstantMessaging::OFF_CALL, TRUE_STR);
487 :
488 54 : if (presence_) {
489 54 : a.emplace(Conf::CONFIG_PRESENCE_STATUS, presence_->isOnline() ? TRUE_STR : FALSE_STR);
490 54 : a.emplace(Conf::CONFIG_PRESENCE_NOTE, presence_->getNote());
491 : }
492 :
493 54 : if (transport_ and transport_->isSecure() and transport_->isConnected()) {
494 0 : const auto& tlsInfos = transport_->getTlsInfos();
495 0 : auto cipher = pj_ssl_cipher_name(tlsInfos.cipher);
496 0 : if (tlsInfos.cipher and not cipher)
497 0 : JAMI_WARN("Unknown cipher: %d", tlsInfos.cipher);
498 0 : a.emplace(libjami::TlsTransport::TLS_CIPHER, cipher ? cipher : "");
499 0 : a.emplace(libjami::TlsTransport::TLS_PEER_CERT, tlsInfos.peerCert->toString());
500 0 : auto ca = tlsInfos.peerCert->issuer;
501 0 : unsigned n = 0;
502 0 : while (ca) {
503 0 : std::ostringstream name_str;
504 0 : name_str << libjami::TlsTransport::TLS_PEER_CA_ << n++;
505 0 : a.emplace(name_str.str(), ca->toString());
506 0 : ca = ca->issuer;
507 0 : }
508 0 : a.emplace(libjami::TlsTransport::TLS_PEER_CA_NUM, std::to_string(n));
509 0 : }
510 :
511 54 : return a;
512 0 : }
513 :
514 : bool
515 4 : SIPAccount::mapPortUPnP()
516 : {
517 4 : dhtnet::upnp::Mapping map(dhtnet::upnp::PortType::UDP, config().publishedPort, config().localPort);
518 4 : map.setNotifyCallback([w = weak()](dhtnet::upnp::Mapping::sharedPtr_t mapRes) {
519 3 : if (auto accPtr = w.lock()) {
520 3 : auto oldPort = static_cast<in_port_t>(accPtr->publishedPortUsed_);
521 3 : bool success = mapRes->getState() == dhtnet::upnp::MappingState::OPEN
522 3 : or mapRes->getState() == dhtnet::upnp::MappingState::IN_PROGRESS;
523 3 : auto newPort = success ? mapRes->getExternalPort() : accPtr->config().publishedPort;
524 3 : if (not success and not accPtr->isRegistered()) {
525 8 : JAMI_WARNING("[Account {:s}] Failed to open port {}: registering SIP account anyway",
526 : accPtr->getAccountID(),
527 : oldPort);
528 2 : accPtr->doRegister1_();
529 2 : return;
530 : }
531 1 : if ((oldPort != newPort) or (accPtr->getRegistrationState() != RegistrationState::REGISTERED)) {
532 0 : if (not accPtr->isRegistered())
533 0 : JAMI_WARNING("[Account {:s}] SIP port {} opened: registering SIP account",
534 : accPtr->getAccountID(),
535 : newPort);
536 : else
537 0 : JAMI_WARNING("[Account {:s}] SIP port changed to {}: re-registering SIP account",
538 : accPtr->getAccountID(),
539 : newPort);
540 0 : accPtr->publishedPortUsed_ = newPort;
541 : } else {
542 1 : accPtr->connectivityChanged();
543 : }
544 :
545 1 : accPtr->doRegister1_();
546 3 : }
547 : });
548 :
549 4 : auto mapRes = upnpCtrl_->reserveMapping(map);
550 4 : if (mapRes and mapRes->getState() == dhtnet::upnp::MappingState::OPEN) {
551 0 : return true;
552 : }
553 :
554 4 : return false;
555 4 : }
556 :
557 : bool
558 0 : SIPAccount::setPushNotificationToken(const std::string& pushDeviceToken)
559 : {
560 0 : JAMI_WARNING("[SIP Account {}] setPushNotificationToken: {}", getAccountID(), pushDeviceToken);
561 0 : if (SIPAccountBase::setPushNotificationToken(pushDeviceToken)) {
562 0 : if (config().enabled) {
563 0 : doUnregister();
564 0 : doRegister();
565 : }
566 0 : return true;
567 : }
568 0 : return false;
569 : }
570 :
571 : bool
572 0 : SIPAccount::setPushNotificationConfig(const std::map<std::string, std::string>& data)
573 : {
574 0 : if (SIPAccountBase::setPushNotificationConfig(data)) {
575 0 : if (config().enabled) {
576 0 : doUnregister();
577 0 : doRegister();
578 : }
579 0 : return true;
580 : }
581 0 : return false;
582 : }
583 :
584 : void
585 0 : SIPAccount::pushNotificationReceived(const std::string& from, const std::map<std::string, std::string>&)
586 : {
587 0 : JAMI_WARNING("[SIP Account {:s}] pushNotificationReceived: {:s}", getAccountID(), from);
588 :
589 0 : if (config().enabled) {
590 0 : doUnregister();
591 0 : doRegister();
592 : }
593 0 : }
594 :
595 : void
596 25 : SIPAccount::doRegister()
597 : {
598 25 : if (not isUsable()) {
599 0 : JAMI_WARN("Account must be enabled and active to register, ignoring");
600 0 : return;
601 : }
602 :
603 100 : JAMI_DEBUG("doRegister {:s}", config_->hostname);
604 :
605 : /* if UPnP is enabled, then wait for IGD to complete registration */
606 25 : if (upnpCtrl_) {
607 4 : JAMI_DBG("UPnP: waiting for IGD to register SIP account");
608 4 : setRegistrationState(RegistrationState::TRYING);
609 4 : if (not mapPortUPnP()) {
610 4 : JAMI_DBG("UPnP: UPNP request failed, try to register SIP account anyway");
611 4 : doRegister1_();
612 : }
613 : } else {
614 21 : doRegister1_();
615 : }
616 : }
617 :
618 : void
619 28 : SIPAccount::doRegister1_()
620 : {
621 : {
622 28 : std::lock_guard lock(configurationMutex_);
623 28 : if (isIP2IP()) {
624 23 : doRegister2_();
625 23 : return;
626 : }
627 28 : }
628 :
629 10 : link_.resolveSrvName(hasServiceRoute() ? getServiceRoute() : config().hostname,
630 5 : config().tlsEnable ? PJSIP_TRANSPORT_TLS : PJSIP_TRANSPORT_UDP,
631 5 : [w = weak()](std::vector<dhtnet::IpAddr> host_ips) {
632 5 : if (auto acc = w.lock()) {
633 5 : std::lock_guard lock(acc->configurationMutex_);
634 5 : if (host_ips.empty()) {
635 0 : JAMI_ERR("Unable to resolve hostname for registration.");
636 0 : acc->setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
637 0 : return;
638 : }
639 5 : acc->hostIp_ = host_ips[0];
640 5 : acc->doRegister2_();
641 10 : }
642 : });
643 : }
644 :
645 : void
646 28 : SIPAccount::doRegister2_()
647 : {
648 28 : if (not isIP2IP() and not hostIp_) {
649 0 : setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
650 0 : JAMI_ERROR("Hostname not resolved.");
651 23 : return;
652 : }
653 :
654 28 : dhtnet::IpAddr bindAddress = createBindingAddress();
655 28 : if (not bindAddress) {
656 0 : setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
657 0 : JAMI_ERROR("Unable to compute address to bind.");
658 0 : return;
659 : }
660 :
661 28 : bool ipv6 = bindAddress.isIpv6();
662 56 : transportType_ = config().tlsEnable ? (ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS)
663 28 : : (ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP);
664 :
665 : // Init TLS settings if the user wants to use TLS
666 28 : if (config().tlsEnable) {
667 0 : JAMI_DEBUG("TLS is enabled for account {}", accountID_);
668 :
669 : // Dropping current calls already using the transport is currently required
670 : // with TLS.
671 0 : hangupCalls();
672 0 : initTlsConfiguration();
673 :
674 0 : if (!tlsListener_) {
675 0 : tlsListener_ = link_.sipTransportBroker->getTlsListener(bindAddress, getTlsSetting());
676 0 : if (!tlsListener_) {
677 0 : setRegistrationState(RegistrationState::ERROR_GENERIC);
678 0 : JAMI_ERROR("Error creating TLS listener.");
679 0 : return;
680 : }
681 : }
682 : } else {
683 28 : tlsListener_.reset();
684 : }
685 :
686 : // In our definition of the ip2ip profile (aka Direct IP Calls),
687 : // no registration should be performed
688 28 : if (isIP2IP()) {
689 : // If we use Tls for IP2IP, transports will be created on connection.
690 23 : if (!config().tlsEnable) {
691 23 : setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress));
692 : }
693 23 : setRegistrationState(RegistrationState::REGISTERED);
694 23 : return;
695 : }
696 :
697 : try {
698 20 : JAMI_WARNING("Creating transport");
699 5 : transport_.reset();
700 5 : if (isTlsEnabled()) {
701 0 : setTransport(link_.sipTransportBroker->getTlsTransport(tlsListener_,
702 0 : hostIp_,
703 0 : config().tlsServerName.empty()
704 0 : ? config().hostname
705 0 : : config().tlsServerName));
706 : } else {
707 5 : setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress));
708 : }
709 5 : if (!transport_)
710 0 : throw VoipLinkException("Unable to create transport");
711 :
712 5 : sendRegister();
713 0 : } catch (const VoipLinkException& e) {
714 0 : JAMI_ERR("%s", e.what());
715 0 : setRegistrationState(RegistrationState::ERROR_GENERIC);
716 0 : return;
717 0 : }
718 :
719 5 : if (presence_ and presence_->isEnabled()) {
720 0 : presence_->subscribeClient(getFromUri(), true); // self presence subscription
721 0 : presence_->sendPresence(true, ""); // attempt to publish whatever the status is.
722 : }
723 : }
724 :
725 : void
726 25 : SIPAccount::doUnregister(bool /* forceShutdownConnections */)
727 : {
728 25 : std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
729 :
730 25 : tlsListener_.reset();
731 :
732 25 : if (!isIP2IP()) {
733 : try {
734 3 : sendUnregister();
735 0 : } catch (const VoipLinkException& e) {
736 0 : JAMI_ERR("doUnregister %s", e.what());
737 0 : }
738 : }
739 :
740 25 : if (transport_)
741 25 : setTransport();
742 25 : resetAutoRegistration();
743 25 : }
744 :
745 : void
746 1 : SIPAccount::connectivityChanged()
747 : {
748 1 : if (not isUsable()) {
749 : // Nothing to do
750 0 : return;
751 : }
752 :
753 1 : doUnregister();
754 1 : if (isUsable())
755 1 : doRegister();
756 : }
757 :
758 : void
759 5 : SIPAccount::sendRegister()
760 : {
761 5 : if (not isUsable()) {
762 0 : JAMI_WARNING("[Account {}] Must be enabled and active to register, ignoring", accountID_);
763 0 : return;
764 : }
765 :
766 5 : bRegister_ = true;
767 5 : setRegistrationState(RegistrationState::TRYING);
768 :
769 5 : pjsip_regc* regc = nullptr;
770 5 : if (pjsip_regc_create(link_.getEndpoint(), (void*) this, ®istration_cb, ®c) != PJ_SUCCESS)
771 0 : throw VoipLinkException("UserAgent: Unable to create regc structure.");
772 :
773 5 : std::string srvUri(getServerUri());
774 5 : pj_str_t pjSrv {(char*) srvUri.data(), (pj_ssize_t) srvUri.size()};
775 :
776 : // Generate the FROM header
777 5 : std::string from(getFromUri());
778 5 : pj_str_t pjFrom(sip_utils::CONST_PJ_STR(from));
779 :
780 : // Get the received header
781 5 : const std::string& received(getReceivedParameter());
782 :
783 5 : std::string contact = getContactHeader();
784 :
785 20 : JAMI_LOG("[Account {}] Using contact header {} in registration", accountID_, contact);
786 :
787 5 : if (transport_) {
788 5 : if (getUPnPActive() or not getPublishedSameasLocal()
789 10 : or (not received.empty() and received != getPublishedAddress())) {
790 0 : pjsip_host_port* via = getViaAddr();
791 0 : JAMI_LOG("Setting VIA sent-by to {:s}:{:d}", sip_utils::as_view(via->host), via->port);
792 :
793 0 : if (pjsip_regc_set_via_sent_by(regc, via, transport_->get()) != PJ_SUCCESS)
794 0 : throw VoipLinkException("Unable to set the \"sent-by\" field");
795 5 : } else if (isStunEnabled()) {
796 0 : if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_->get()) != PJ_SUCCESS)
797 0 : throw VoipLinkException("Unable to set the \"sent-by\" field");
798 : }
799 : }
800 :
801 5 : pj_status_t status = PJ_SUCCESS;
802 5 : pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
803 :
804 5 : if ((status = pjsip_regc_init(regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, getRegistrationExpire()))
805 5 : != PJ_SUCCESS) {
806 0 : JAMI_ERR("pjsip_regc_init failed with error %d: %s", status, sip_utils::sip_strerror(status).c_str());
807 0 : throw VoipLinkException("Unable to initialize account registration structure");
808 : }
809 :
810 5 : if (hasServiceRoute())
811 0 : pjsip_regc_set_route_set(regc, sip_utils::createRouteSet(getServiceRoute(), link_.getPool()));
812 :
813 5 : pjsip_regc_set_credentials(regc, getCredentialCount(), getCredInfo());
814 :
815 : pjsip_hdr hdr_list;
816 5 : pj_list_init(&hdr_list);
817 5 : auto pjUserAgent = CONST_PJ_STR(getUserAgentName());
818 5 : constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent");
819 :
820 5 : pjsip_generic_string_hdr* h = pjsip_generic_string_hdr_create(link_.getPool(), &STR_USER_AGENT, &pjUserAgent);
821 5 : pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
822 5 : pjsip_regc_add_headers(regc, &hdr_list);
823 :
824 : pjsip_tx_data* tdata;
825 :
826 5 : if (pjsip_regc_register(regc, isRegistrationRefreshEnabled(), &tdata) != PJ_SUCCESS)
827 0 : throw VoipLinkException("Unable to initialize transaction data for account registration");
828 :
829 5 : const pjsip_tpselector tp_sel = getTransportSelector();
830 5 : if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
831 0 : throw VoipLinkException("Unable to set transport");
832 :
833 5 : if (tp_sel.u.transport)
834 5 : setUpTransmissionData(tdata, tp_sel.u.transport->key.type);
835 :
836 : // pjsip_regc_send increment the transport ref count by one,
837 5 : if ((status = pjsip_regc_send(regc, tdata)) != PJ_SUCCESS) {
838 0 : JAMI_ERROR("pjsip_regc_send failed with error {:d}: {}", status, sip_utils::sip_strerror(status));
839 0 : throw VoipLinkException("Unable to send account registration request");
840 : }
841 :
842 5 : setRegistrationInfo(regc);
843 5 : }
844 :
845 : void
846 8 : SIPAccount::setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType)
847 : {
848 8 : if (hostIp_) {
849 8 : auto ai = &tdata->dest_info;
850 8 : ai->name = pj_strdup3(tdata->pool, config().hostname.c_str());
851 8 : ai->addr.count = 1;
852 8 : ai->addr.entry[0].type = (pjsip_transport_type_e) transportKeyType;
853 8 : pj_memcpy(&ai->addr.entry[0].addr, hostIp_.pjPtr(), sizeof(pj_sockaddr));
854 8 : ai->addr.entry[0].addr_len = hostIp_.getLength();
855 8 : ai->cur_addr = 0;
856 : }
857 8 : }
858 :
859 : void
860 3 : SIPAccount::onRegister(pjsip_regc_cbparam* param)
861 : {
862 3 : if (param->regc != getRegistrationInfo())
863 0 : return;
864 :
865 3 : if (param->status != PJ_SUCCESS) {
866 0 : JAMI_ERROR("[Account {}] SIP registration error {:d}", accountID_, param->status);
867 0 : destroyRegistrationInfo();
868 0 : setRegistrationState(RegistrationState::ERROR_GENERIC, param->code);
869 3 : } else if (param->code < 0 || param->code >= 300) {
870 0 : JAMI_ERROR("[Account {}] SIP registration failed, status={:d} ({:s})",
871 : accountID_,
872 : param->code,
873 : sip_utils::as_view(param->reason));
874 0 : destroyRegistrationInfo();
875 0 : switch (param->code) {
876 0 : case PJSIP_SC_FORBIDDEN:
877 0 : setRegistrationState(RegistrationState::ERROR_AUTH, param->code);
878 0 : break;
879 0 : case PJSIP_SC_NOT_FOUND:
880 0 : setRegistrationState(RegistrationState::ERROR_HOST, param->code);
881 0 : break;
882 0 : case PJSIP_SC_REQUEST_TIMEOUT:
883 0 : setRegistrationState(RegistrationState::ERROR_HOST, param->code);
884 0 : break;
885 0 : case PJSIP_SC_SERVICE_UNAVAILABLE:
886 0 : setRegistrationState(RegistrationState::ERROR_SERVICE_UNAVAILABLE, param->code);
887 0 : break;
888 0 : default:
889 0 : setRegistrationState(RegistrationState::ERROR_GENERIC, param->code);
890 : }
891 3 : } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
892 : // Update auto registration flag
893 3 : resetAutoRegistration();
894 :
895 3 : if (param->expiration < 1) {
896 0 : destroyRegistrationInfo();
897 0 : JAMI_DBG("Unregistration success");
898 0 : setRegistrationState(RegistrationState::UNREGISTERED, param->code);
899 : } else {
900 : /* TODO Check and update SIP outbound status first, since the result
901 : * will determine if we should update re-registration
902 : */
903 : // update_rfc5626_status(acc, param->rdata);
904 :
905 3 : if (config().allowIPAutoRewrite and checkNATAddress(param, link_.getPool()))
906 3 : JAMI_WARN("New contact: %s", getContactHeader().c_str());
907 :
908 : /* TODO Check and update Service-Route header */
909 3 : if (hasServiceRoute())
910 0 : pjsip_regc_set_route_set(param->regc, sip_utils::createRouteSet(getServiceRoute(), link_.getPool()));
911 :
912 3 : setRegistrationState(RegistrationState::REGISTERED, param->code);
913 : }
914 : }
915 :
916 : /* Check if we need to auto retry registration. Basically, registration
917 : * failure codes triggering auto-retry are those of temporal failures
918 : * considered to be recoverable in relatively short term.
919 : */
920 3 : switch (param->code) {
921 0 : case PJSIP_SC_REQUEST_TIMEOUT:
922 : case PJSIP_SC_INTERNAL_SERVER_ERROR:
923 : case PJSIP_SC_BAD_GATEWAY:
924 : case PJSIP_SC_SERVICE_UNAVAILABLE:
925 : case PJSIP_SC_SERVER_TIMEOUT:
926 0 : scheduleReregistration();
927 0 : break;
928 :
929 3 : default:
930 : /* Global failure */
931 3 : if (PJSIP_IS_STATUS_IN_CLASS(param->code, 600))
932 0 : scheduleReregistration();
933 : }
934 :
935 3 : if (param->expiration != config().registrationExpire) {
936 0 : JAMI_DBG("Registrar returned EXPIRE value [%u s] different from the requested [%u s]",
937 : param->expiration,
938 : config().registrationExpire);
939 : // NOTE: We don't alter the EXPIRE set by the user even if the registrar
940 : // returned a different value. PJSIP lib will set the proper timer for
941 : // the refresh, if the auto-regisration is enabled.
942 : }
943 : }
944 :
945 : void
946 3 : SIPAccount::sendUnregister()
947 : {
948 : // This may occurs if account failed to register and is in state INVALID
949 3 : if (!isRegistered()) {
950 0 : setRegistrationState(RegistrationState::UNREGISTERED);
951 0 : return;
952 : }
953 :
954 3 : bRegister_ = false;
955 3 : pjsip_regc* regc = getRegistrationInfo();
956 3 : if (!regc)
957 0 : throw VoipLinkException("Registration structure is NULL");
958 :
959 3 : pjsip_tx_data* tdata = nullptr;
960 3 : if (pjsip_regc_unregister(regc, &tdata) != PJ_SUCCESS)
961 0 : throw VoipLinkException("Unable to unregister SIP account");
962 :
963 3 : const pjsip_tpselector tp_sel = getTransportSelector();
964 3 : if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
965 0 : throw VoipLinkException("Unable to set transport");
966 :
967 3 : if (tp_sel.u.transport)
968 3 : setUpTransmissionData(tdata, tp_sel.u.transport->key.type);
969 :
970 : pj_status_t status;
971 3 : if ((status = pjsip_regc_send(regc, tdata)) != PJ_SUCCESS) {
972 0 : JAMI_ERR("pjsip_regc_send failed with error %d: %s", status, sip_utils::sip_strerror(status).c_str());
973 0 : throw VoipLinkException("Unable to send request to unregister SIP account");
974 : }
975 : }
976 :
977 : pj_uint32_t
978 0 : SIPAccount::tlsProtocolFromString(const std::string& method)
979 : {
980 0 : if (method == "Default")
981 0 : return PJSIP_SSL_DEFAULT_PROTO;
982 0 : if (method == "TLSv1.2")
983 0 : return PJ_SSL_SOCK_PROTO_TLS1_2;
984 0 : if (method == "TLSv1.1")
985 0 : return PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_1;
986 0 : if (method == "TLSv1")
987 0 : return PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_1 | PJ_SSL_SOCK_PROTO_TLS1;
988 0 : return PJSIP_SSL_DEFAULT_PROTO;
989 : }
990 :
991 : /**
992 : * PJSIP aborts if our cipher list exceeds 1000 characters
993 : */
994 : void
995 0 : SIPAccount::trimCiphers()
996 : {
997 0 : size_t sum = 0;
998 0 : unsigned count = 0;
999 : static const size_t MAX_CIPHERS_STRLEN = 1000;
1000 0 : for (const auto& item : ciphers_) {
1001 0 : sum += strlen(pj_ssl_cipher_name(item));
1002 0 : if (sum > MAX_CIPHERS_STRLEN)
1003 0 : break;
1004 0 : ++count;
1005 : }
1006 0 : ciphers_.resize(count);
1007 0 : }
1008 :
1009 : void
1010 0 : SIPAccount::initTlsConfiguration()
1011 : {
1012 0 : pjsip_tls_setting_default(&tlsSetting_);
1013 0 : const auto& conf = config();
1014 0 : tlsSetting_.proto = tlsProtocolFromString(conf.tlsMethod);
1015 :
1016 : // Determine the cipher list supported on this machine
1017 0 : CipherArray avail_ciphers(256);
1018 0 : unsigned cipherNum = avail_ciphers.size();
1019 0 : if (pj_ssl_cipher_get_availables(&avail_ciphers.front(), &cipherNum) != PJ_SUCCESS)
1020 0 : JAMI_ERR("Unable to determine cipher list on this system");
1021 0 : avail_ciphers.resize(cipherNum);
1022 :
1023 0 : ciphers_.clear();
1024 0 : std::string_view stream(conf.tlsCiphers), item;
1025 0 : while (jami::getline(stream, item, ' ')) {
1026 0 : std::string cipher(item);
1027 0 : auto item_cid = pj_ssl_cipher_id(cipher.c_str());
1028 0 : if (item_cid != PJ_TLS_UNKNOWN_CIPHER) {
1029 0 : JAMI_WARN("Valid cipher: %s", cipher.c_str());
1030 0 : ciphers_.push_back(item_cid);
1031 : } else
1032 0 : JAMI_ERR("Invalid cipher: %s", cipher.c_str());
1033 0 : }
1034 :
1035 0 : ciphers_.erase(std::remove_if(ciphers_.begin(),
1036 : ciphers_.end(),
1037 0 : [&](pj_ssl_cipher c) {
1038 0 : return std::find(avail_ciphers.cbegin(), avail_ciphers.cend(), c)
1039 0 : == avail_ciphers.cend();
1040 : }),
1041 0 : ciphers_.end());
1042 :
1043 0 : trimCiphers();
1044 :
1045 0 : tlsSetting_.ca_list_file = CONST_PJ_STR(conf.tlsCaListFile);
1046 0 : tlsSetting_.cert_file = CONST_PJ_STR(conf.tlsCaListFile);
1047 0 : tlsSetting_.privkey_file = CONST_PJ_STR(conf.tlsPrivateKeyFile);
1048 0 : tlsSetting_.password = CONST_PJ_STR(conf.tlsPassword);
1049 :
1050 0 : JAMI_DBG("Using %zu ciphers", ciphers_.size());
1051 0 : tlsSetting_.ciphers_num = ciphers_.size();
1052 0 : if (tlsSetting_.ciphers_num > 0) {
1053 0 : tlsSetting_.ciphers = &ciphers_.front();
1054 : }
1055 :
1056 0 : tlsSetting_.verify_server = conf.tlsVerifyServer;
1057 0 : tlsSetting_.verify_client = conf.tlsVerifyClient;
1058 0 : tlsSetting_.require_client_cert = conf.tlsRequireClientCertificate;
1059 0 : pjsip_cfg()->endpt.disable_secure_dlg_check = conf.tlsDisableSecureDlgCheck;
1060 0 : tlsSetting_.timeout.sec = conf.tlsNegotiationTimeout;
1061 :
1062 0 : tlsSetting_.qos_type = PJ_QOS_TYPE_BEST_EFFORT;
1063 0 : tlsSetting_.qos_ignore_error = PJ_TRUE;
1064 0 : }
1065 :
1066 : void
1067 24 : SIPAccount::initStunConfiguration()
1068 : {
1069 24 : std::string_view stunServer(config().stunServer);
1070 24 : auto pos = stunServer.find(':');
1071 24 : if (pos == std::string_view::npos) {
1072 24 : stunServerName_ = sip_utils::CONST_PJ_STR(stunServer);
1073 24 : stunPort_ = PJ_STUN_PORT;
1074 : } else {
1075 0 : stunServerName_ = sip_utils::CONST_PJ_STR(stunServer.substr(0, pos));
1076 0 : auto serverPort = stunServer.substr(pos + 1);
1077 0 : stunPort_ = to_int<uint16_t>(serverPort);
1078 : }
1079 24 : }
1080 :
1081 : void
1082 24 : SIPAccount::loadConfig()
1083 : {
1084 24 : SIPAccountBase::loadConfig();
1085 24 : setCredentials(config().credentials);
1086 24 : enablePresence(config().presenceEnabled);
1087 24 : initStunConfiguration();
1088 24 : if (config().tlsEnable) {
1089 0 : initTlsConfiguration();
1090 0 : transportType_ = PJSIP_TRANSPORT_TLS;
1091 : } else
1092 24 : transportType_ = PJSIP_TRANSPORT_UDP;
1093 24 : if (registrationState_ == RegistrationState::UNLOADED)
1094 24 : setRegistrationState(RegistrationState::UNREGISTERED);
1095 24 : }
1096 :
1097 : bool
1098 33 : SIPAccount::fullMatch(std::string_view username, std::string_view hostname) const
1099 : {
1100 33 : return userMatch(username) and hostnameMatch(hostname);
1101 : }
1102 :
1103 : bool
1104 57 : SIPAccount::userMatch(std::string_view username) const
1105 : {
1106 57 : return !username.empty() and username == config().username;
1107 : }
1108 :
1109 : bool
1110 39 : SIPAccount::hostnameMatch(std::string_view hostname) const
1111 : {
1112 39 : if (hostname == config().hostname)
1113 9 : return true;
1114 30 : const auto a = dhtnet::ip_utils::getAddrList(hostname);
1115 30 : const auto b = dhtnet::ip_utils::getAddrList(config().hostname);
1116 30 : return dhtnet::ip_utils::haveCommonAddr(a, b);
1117 30 : }
1118 :
1119 : bool
1120 18 : SIPAccount::proxyMatch(std::string_view hostname) const
1121 : {
1122 18 : if (hostname == config().serviceRoute)
1123 0 : return true;
1124 18 : const auto a = dhtnet::ip_utils::getAddrList(hostname);
1125 18 : const auto b = dhtnet::ip_utils::getAddrList(config().hostname);
1126 18 : return dhtnet::ip_utils::haveCommonAddr(a, b);
1127 18 : }
1128 :
1129 : std::string
1130 3 : SIPAccount::getLoginName()
1131 : {
1132 : #ifndef _WIN32
1133 3 : struct passwd* user_info = getpwuid(getuid());
1134 3 : return user_info ? user_info->pw_name : "";
1135 : #else
1136 : DWORD size = UNLEN + 1;
1137 : TCHAR username[UNLEN + 1];
1138 : std::string uname;
1139 : if (GetUserName((TCHAR*) username, &size)) {
1140 : uname = jami::to_string(username);
1141 : }
1142 : return uname;
1143 : #endif
1144 : }
1145 :
1146 : std::string
1147 15 : SIPAccount::getFromUri() const
1148 : {
1149 15 : std::string scheme;
1150 15 : std::string transport;
1151 :
1152 : // Get login name if username is not specified
1153 15 : const auto& conf = config();
1154 15 : std::string username(conf.username.empty() ? getLoginName() : conf.username);
1155 15 : std::string hostname(conf.hostname);
1156 :
1157 : // UDP does not require the transport specification
1158 15 : if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1159 0 : scheme = "sips:";
1160 0 : transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1161 : } else
1162 15 : scheme = "sip:";
1163 :
1164 : // Get machine hostname if not provided
1165 15 : if (hostname.empty()) {
1166 9 : hostname = sip_utils::as_view(*pj_gethostname());
1167 : }
1168 :
1169 15 : if (dhtnet::IpAddr::isIpv6(hostname))
1170 0 : hostname = dhtnet::IpAddr(hostname).toString(false, true);
1171 :
1172 30 : std::string uri = "<" + scheme + username + "@" + hostname + transport + ">";
1173 15 : if (not conf.displayName.empty())
1174 30 : return "\"" + conf.displayName + "\" " + uri;
1175 0 : return uri;
1176 15 : }
1177 :
1178 : std::string
1179 21 : SIPAccount::getToUri(const std::string& username) const
1180 : {
1181 21 : std::string scheme;
1182 21 : std::string transport;
1183 21 : std::string hostname;
1184 :
1185 : // UDP does not require the transport specification
1186 21 : if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1187 0 : scheme = "sips:";
1188 0 : transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1189 : } else
1190 21 : scheme = "sip:";
1191 :
1192 : // Check if scheme is already specified
1193 21 : if (username.find("sip") != std::string::npos)
1194 1 : scheme = "";
1195 :
1196 : // Check if hostname is already specified
1197 21 : if (username.find('@') == std::string::npos)
1198 4 : hostname = config().hostname;
1199 :
1200 21 : if (not hostname.empty() and dhtnet::IpAddr::isIpv6(hostname))
1201 0 : hostname = dhtnet::IpAddr(hostname).toString(false, true);
1202 :
1203 21 : auto ltSymbol = username.find('<') == std::string::npos ? "<" : "";
1204 21 : auto gtSymbol = username.find('>') == std::string::npos ? ">" : "";
1205 :
1206 63 : return ltSymbol + scheme + username + (hostname.empty() ? "" : "@") + hostname + transport + gtSymbol;
1207 21 : }
1208 :
1209 : std::string
1210 5 : SIPAccount::getServerUri() const
1211 : {
1212 5 : std::string scheme;
1213 5 : std::string transport;
1214 :
1215 : // UDP does not require the transport specification
1216 5 : if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1217 0 : scheme = "sips:";
1218 0 : transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1219 : } else {
1220 5 : scheme = "sip:";
1221 : }
1222 :
1223 5 : std::string host;
1224 5 : if (dhtnet::IpAddr::isIpv6(config().hostname))
1225 0 : host = dhtnet::IpAddr(config().hostname).toString(false, true);
1226 : else
1227 5 : host = config().hostname;
1228 :
1229 15 : return "<" + scheme + host + transport + ">";
1230 5 : }
1231 :
1232 : dhtnet::IpAddr
1233 3 : SIPAccount::getContactAddress() const
1234 : {
1235 3 : std::lock_guard lock(contactMutex_);
1236 3 : return contactAddress_;
1237 3 : }
1238 :
1239 : std::string
1240 41 : SIPAccount::getContactHeader() const
1241 : {
1242 41 : std::lock_guard lock(contactMutex_);
1243 82 : return contactHeader_;
1244 41 : }
1245 :
1246 : void
1247 27 : SIPAccount::updateContactHeader()
1248 : {
1249 27 : std::lock_guard lock(contactMutex_);
1250 :
1251 27 : if (not transport_ or not transport_->get()) {
1252 0 : JAMI_ERR("Transport not created yet");
1253 0 : return;
1254 : }
1255 :
1256 27 : if (not contactAddress_) {
1257 0 : JAMI_ERR("Invalid contact address: %s", contactAddress_.toString(true).c_str());
1258 0 : return;
1259 : }
1260 :
1261 54 : auto contactHdr = printContactHeader(config().username,
1262 27 : config().displayName,
1263 0 : contactAddress_.toString(false, true),
1264 54 : contactAddress_.getPort(),
1265 27 : PJSIP_TRANSPORT_IS_SECURE(transport_->get()),
1266 81 : config().deviceKey);
1267 :
1268 27 : contactHeader_ = std::move(contactHdr);
1269 27 : }
1270 :
1271 : bool
1272 27 : SIPAccount::initContactAddress()
1273 : {
1274 : // This method tries to determine the address to be used in the
1275 : // contact header using the available information (current transport,
1276 : // UPNP, STUN, …). The contact address may be updated after the
1277 : // registration using information sent by the registrar in the SIP
1278 : // messages (see checkNATAddress).
1279 :
1280 27 : if (not transport_ or not transport_->get()) {
1281 0 : JAMI_ERR("Transport not created yet");
1282 0 : return {};
1283 : }
1284 :
1285 : // The transport type must be specified, in our case START_OTHER refers to stun transport
1286 27 : pjsip_transport_type_e transportType = transportType_;
1287 :
1288 27 : if (transportType == PJSIP_TRANSPORT_START_OTHER)
1289 0 : transportType = PJSIP_TRANSPORT_UDP;
1290 :
1291 27 : std::string address;
1292 : pj_uint16_t port;
1293 :
1294 : // Init the address to the local address.
1295 27 : link_.findLocalAddressFromTransport(transport_->get(), transportType, config().hostname, address, port);
1296 :
1297 27 : if (getUPnPActive() and getUPnPIpAddress()) {
1298 0 : address = getUPnPIpAddress().toString();
1299 0 : port = publishedPortUsed_;
1300 0 : useUPnPAddressPortInVIA();
1301 0 : JAMI_DBG("Using UPnP address %s and port %d", address.c_str(), port);
1302 27 : } else if (not config().publishedSameasLocal) {
1303 0 : address = getPublishedIpAddress().toString();
1304 0 : port = config().publishedPort;
1305 0 : JAMI_DBG("Using published address %s and port %d", address.c_str(), port);
1306 27 : } else if (config().stunEnabled) {
1307 0 : auto success = link_.findLocalAddressFromSTUN(transport_->get(), &stunServerName_, stunPort_, address, port);
1308 0 : if (not success)
1309 0 : emitSignal<libjami::ConfigurationSignal::StunStatusFailed>(getAccountID());
1310 0 : setPublishedAddress({address});
1311 0 : publishedPortUsed_ = port;
1312 0 : usePublishedAddressPortInVIA();
1313 : } else {
1314 27 : if (!receivedParameter_.empty()) {
1315 0 : address = receivedParameter_;
1316 0 : JAMI_DBG("Using received address %s", address.c_str());
1317 : }
1318 :
1319 27 : if (rPort_ > 0) {
1320 0 : port = rPort_;
1321 0 : JAMI_DBG("Using received port %d", port);
1322 : }
1323 : }
1324 :
1325 27 : std::lock_guard lock(contactMutex_);
1326 27 : contactAddress_ = dhtnet::IpAddr(address);
1327 27 : contactAddress_.setPort(port);
1328 :
1329 27 : return contactAddress_;
1330 27 : }
1331 :
1332 : std::string
1333 30 : SIPAccount::printContactHeader(const std::string& username,
1334 : const std::string& displayName,
1335 : const std::string& address,
1336 : pj_uint16_t port,
1337 : bool secure,
1338 : const std::string& deviceKey)
1339 : {
1340 : // This method generates SIP contact header field, with push
1341 : // notification parameters if any.
1342 : // Example without push notification:
1343 : // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls>
1344 : // Example with push notification:
1345 : // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls;pn-provider=XXX;pn-param=YYY;pn-prid=ZZZ>
1346 :
1347 59 : std::string quotedDisplayName = displayName.empty() ? "" : "\"" + displayName + "\" ";
1348 :
1349 30 : std::ostringstream contact;
1350 30 : auto scheme = secure ? "sips" : "sip";
1351 30 : auto transport = secure ? ";transport=tls" : "";
1352 :
1353 30 : contact << quotedDisplayName << "<" << scheme << ":" << username << (username.empty() ? "" : "@") << address << ":"
1354 30 : << port << transport;
1355 :
1356 30 : if (not deviceKey.empty()) {
1357 : contact
1358 : #if defined(__ANDROID__)
1359 : << ";pn-provider=" << PN_FCM
1360 : #elif defined(__Apple__)
1361 : << ";pn-provider=" << PN_APNS
1362 : #endif
1363 0 : << ";pn-param=" << ";pn-prid=" << deviceKey;
1364 : }
1365 30 : contact << ">";
1366 :
1367 60 : return contact.str();
1368 30 : }
1369 :
1370 : pjsip_host_port
1371 0 : SIPAccount::getHostPortFromSTUN(pj_pool_t* pool)
1372 : {
1373 0 : std::string addr;
1374 : pj_uint16_t port;
1375 0 : auto success = link_.findLocalAddressFromSTUN(transport_ ? transport_->get() : nullptr,
1376 : &stunServerName_,
1377 0 : stunPort_,
1378 : addr,
1379 : port);
1380 0 : if (not success)
1381 0 : emitSignal<libjami::ConfigurationSignal::StunStatusFailed>(getAccountID());
1382 : pjsip_host_port result;
1383 0 : pj_strdup2(pool, &result.host, addr.c_str());
1384 0 : result.port = port;
1385 0 : return result;
1386 0 : }
1387 :
1388 : const std::vector<std::string>&
1389 0 : SIPAccount::getSupportedTlsCiphers()
1390 : {
1391 : // Currently, both OpenSSL and GNUTLS implementations are static
1392 : // reloading this for each account is unnecessary
1393 0 : static std::vector<std::string> availCiphers {};
1394 :
1395 : // LIMITATION Assume the size might change, if there aren't any ciphers,
1396 : // this will cause the cache to be repopulated at each call for nothing.
1397 0 : if (availCiphers.empty()) {
1398 0 : unsigned cipherNum = 256;
1399 0 : CipherArray avail_ciphers(cipherNum);
1400 0 : if (pj_ssl_cipher_get_availables(&avail_ciphers.front(), &cipherNum) != PJ_SUCCESS)
1401 0 : JAMI_ERR("Unable to determine cipher list on this system");
1402 0 : avail_ciphers.resize(cipherNum);
1403 0 : availCiphers.reserve(cipherNum);
1404 0 : for (const auto& item : avail_ciphers) {
1405 0 : if (item > 0) // 0 doesn't have a name
1406 0 : availCiphers.push_back(pj_ssl_cipher_name(item));
1407 : }
1408 0 : }
1409 0 : return availCiphers;
1410 : }
1411 :
1412 : const std::vector<std::string>&
1413 0 : SIPAccount::getSupportedTlsProtocols()
1414 : {
1415 0 : static std::vector<std::string> availProtos {VALID_TLS_PROTOS, VALID_TLS_PROTOS + std::size(VALID_TLS_PROTOS)};
1416 0 : return availProtos;
1417 : }
1418 :
1419 : void
1420 24 : SIPAccount::setCredentials(const std::vector<SipAccountConfig::Credentials>& creds)
1421 : {
1422 24 : cred_.clear();
1423 24 : cred_.reserve(creds.size());
1424 24 : bool md5HashingEnabled = Manager::instance().preferences.getMd5Hash();
1425 :
1426 48 : for (auto& c : creds) {
1427 48 : cred_.emplace_back(pjsip_cred_info {/*.realm = */ CONST_PJ_STR(c.realm),
1428 : /*.scheme = */ CONST_PJ_STR("digest"),
1429 24 : /*.username = */ CONST_PJ_STR(c.username),
1430 : /*.data_type = */
1431 24 : (md5HashingEnabled ? PJSIP_CRED_DATA_DIGEST : PJSIP_CRED_DATA_PLAIN_PASSWD),
1432 : /*.data = */
1433 24 : CONST_PJ_STR(md5HashingEnabled ? c.password_h : c.password),
1434 : /*.algorithm_type = */ PJSIP_AUTH_ALGORITHM_NOT_SET,
1435 : /*.ext = */ {}});
1436 : }
1437 24 : }
1438 :
1439 : void
1440 59 : SIPAccount::setRegistrationState(RegistrationState state, int details_code, const std::string& /*detail_str*/)
1441 : {
1442 59 : std::string details_str;
1443 59 : const pj_str_t* description = pjsip_get_status_text(details_code);
1444 59 : if (description)
1445 59 : details_str = sip_utils::as_view(*description);
1446 59 : registrationStateDetailed_ = {details_code, details_str};
1447 59 : SIPAccountBase::setRegistrationState(state, details_code, details_str);
1448 59 : }
1449 :
1450 : bool
1451 182 : SIPAccount::isIP2IP() const
1452 : {
1453 182 : return config().hostname.empty();
1454 : }
1455 :
1456 : SIPPresence*
1457 0 : SIPAccount::getPresence() const
1458 : {
1459 0 : return presence_;
1460 : }
1461 :
1462 : /**
1463 : * Enable the presence module
1464 : */
1465 : void
1466 24 : SIPAccount::enablePresence(const bool& enabled)
1467 : {
1468 24 : if (!presence_) {
1469 0 : JAMI_ERR("Presence not initialized");
1470 0 : return;
1471 : }
1472 :
1473 96 : JAMI_LOG("[Account {}] Presence enabled: {}.", accountID_, enabled ? TRUE_STR : FALSE_STR);
1474 :
1475 24 : presence_->enable(enabled);
1476 : }
1477 :
1478 : /**
1479 : * Set the presence (PUBLISH/SUBSCRIBE) support flags
1480 : * and process the change.
1481 : */
1482 : void
1483 0 : SIPAccount::supportPresence(int function, bool enabled)
1484 : {
1485 0 : if (!presence_) {
1486 0 : JAMI_ERR("Presence not initialized");
1487 0 : return;
1488 : }
1489 :
1490 0 : if (presence_->isSupported(function) == enabled)
1491 0 : return;
1492 :
1493 0 : JAMI_LOG("[Account {}] Presence support ({}: {}).",
1494 : accountID_,
1495 : function == PRESENCE_FUNCTION_PUBLISH ? "publish" : "subscribe",
1496 : enabled ? TRUE_STR : FALSE_STR);
1497 0 : presence_->support(function, enabled);
1498 :
1499 : // force presence to disable when nothing is supported
1500 0 : if (not presence_->isSupported(PRESENCE_FUNCTION_PUBLISH)
1501 0 : and not presence_->isSupported(PRESENCE_FUNCTION_SUBSCRIBE))
1502 0 : enablePresence(false);
1503 :
1504 0 : Manager::instance().saveConfig();
1505 : // FIXME: bad signal used here, we need a global config changed signal.
1506 0 : emitSignal<libjami::ConfigurationSignal::AccountsChanged>();
1507 : }
1508 :
1509 : MatchRank
1510 33 : SIPAccount::matches(std::string_view userName, std::string_view server) const
1511 : {
1512 33 : if (fullMatch(userName, server)) {
1513 24 : JAMI_LOG("Matching account ID in request is a fullmatch {:s}@{:s}", userName, server);
1514 6 : return MatchRank::FULL;
1515 27 : } else if (hostnameMatch(server)) {
1516 12 : JAMI_LOG("Matching account ID in request with hostname {:s}", server);
1517 3 : return MatchRank::PARTIAL;
1518 24 : } else if (userMatch(userName)) {
1519 24 : JAMI_LOG("Matching account ID in request with username {:s}", userName);
1520 6 : return MatchRank::PARTIAL;
1521 18 : } else if (proxyMatch(server)) {
1522 0 : JAMI_LOG("Matching account ID in request with proxy {:s}", server);
1523 0 : return MatchRank::PARTIAL;
1524 : } else {
1525 18 : return MatchRank::NONE;
1526 : }
1527 : }
1528 :
1529 : void
1530 27 : SIPAccount::destroyRegistrationInfo()
1531 : {
1532 27 : if (!regc_)
1533 22 : return;
1534 5 : pjsip_regc_destroy(regc_);
1535 5 : regc_ = nullptr;
1536 : }
1537 :
1538 : void
1539 28 : SIPAccount::resetAutoRegistration()
1540 : {
1541 28 : auto_rereg_.active = PJ_FALSE;
1542 28 : auto_rereg_.attempt_cnt = 0;
1543 28 : if (auto_rereg_.timer.user_data) {
1544 0 : delete ((std::weak_ptr<SIPAccount>*) auto_rereg_.timer.user_data);
1545 0 : auto_rereg_.timer.user_data = nullptr;
1546 : }
1547 28 : }
1548 :
1549 : bool
1550 3 : SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool)
1551 : {
1552 12 : JAMI_LOG("[Account {}] Checking IP route after the registration", accountID_);
1553 :
1554 3 : pjsip_transport* tp = param->rdata->tp_info.transport;
1555 :
1556 : /* Get the received and rport info */
1557 3 : pjsip_via_hdr* via = param->rdata->msg_info.via;
1558 3 : int rport = 0;
1559 3 : if (via->rport_param < 1) {
1560 : /* Remote doesn't support rport */
1561 0 : rport = via->sent_by.port;
1562 0 : if (rport == 0) {
1563 : pjsip_transport_type_e tp_type;
1564 0 : tp_type = (pjsip_transport_type_e) tp->key.type;
1565 0 : rport = pjsip_transport_get_default_port_for_type(tp_type);
1566 : }
1567 : } else {
1568 3 : rport = via->rport_param;
1569 : }
1570 :
1571 3 : const pj_str_t* via_addr = via->recvd_param.slen != 0 ? &via->recvd_param : &via->sent_by.host;
1572 3 : std::string via_addrstr(sip_utils::as_view(*via_addr));
1573 : /* Enclose IPv6 address in square brackets */
1574 3 : if (dhtnet::IpAddr::isIpv6(via_addrstr))
1575 0 : via_addrstr = dhtnet::IpAddr(via_addrstr).toString(false, true);
1576 :
1577 3 : JAMI_DBG("Checking received VIA address: %s", via_addrstr.c_str());
1578 :
1579 3 : if (via_addr_.host.slen == 0 or via_tp_ != tp) {
1580 2 : if (pj_strcmp(&via_addr_.host, via_addr))
1581 2 : pj_strdup(pool, &via_addr_.host, via_addr);
1582 :
1583 : // Update Via header
1584 2 : via_addr_.port = rport;
1585 2 : via_tp_ = tp;
1586 2 : pjsip_regc_set_via_sent_by(regc_, &via_addr_, via_tp_);
1587 : }
1588 :
1589 : // Set published Ip address
1590 3 : setPublishedAddress(dhtnet::IpAddr(via_addrstr));
1591 :
1592 : /* Compare received and rport with the URI in our registration */
1593 3 : dhtnet::IpAddr contact_addr = getContactAddress();
1594 :
1595 : // TODO. Why note save the port in contact URI/header?
1596 3 : if (contact_addr.getPort() == 0) {
1597 : pjsip_transport_type_e tp_type;
1598 0 : tp_type = (pjsip_transport_type_e) tp->key.type;
1599 0 : contact_addr.setPort(pjsip_transport_get_default_port_for_type(tp_type));
1600 : }
1601 :
1602 : /* Convert IP address strings into sockaddr for comparison.
1603 : * (http://trac.pjsip.org/repos/ticket/863)
1604 : */
1605 3 : bool matched = false;
1606 3 : dhtnet::IpAddr recv_addr {};
1607 3 : auto status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr, recv_addr.pjPtr());
1608 3 : recv_addr.setPort(rport);
1609 3 : if (status == PJ_SUCCESS) {
1610 : // Compare the addresses as sockaddr according to the ticket above
1611 3 : matched = contact_addr == recv_addr;
1612 : } else {
1613 : // Compare the addresses as string, as before
1614 0 : auto pjContactAddr = sip_utils::CONST_PJ_STR(contact_addr.toString());
1615 0 : matched = (contact_addr.getPort() == rport and pj_stricmp(&pjContactAddr, via_addr) == 0);
1616 : }
1617 :
1618 3 : if (matched) {
1619 : // Address doesn't change
1620 0 : return false;
1621 : }
1622 :
1623 : /* Get server IP address */
1624 3 : dhtnet::IpAddr srv_ip = {std::string_view(param->rdata->pkt_info.src_name)};
1625 :
1626 : /* At this point we've detected that the address as seen by registrar.
1627 : * has changed.
1628 : */
1629 :
1630 : /* Do not switch if both Contact and server's IP address are
1631 : * public but response contains private IP. A NAT in the middle
1632 : * might have messed up with the SIP packets. See:
1633 : * http://trac.pjsip.org/repos/ticket/643
1634 : *
1635 : * This exception can be disabled by setting allow_contact_rewrite
1636 : * to 2. In this case, the switch will always be done whenever there
1637 : * is difference in the IP address in the response.
1638 : */
1639 3 : if (not contact_addr.isPrivate() and not srv_ip.isPrivate() and recv_addr.isPrivate()) {
1640 : /* Don't switch */
1641 0 : return false;
1642 : }
1643 :
1644 : /* Also don't switch if only the port number part is different, and
1645 : * the Via received address is private.
1646 : * See http://trac.pjsip.org/repos/ticket/864
1647 : */
1648 3 : if (contact_addr == recv_addr and recv_addr.isPrivate()) {
1649 : /* Don't switch */
1650 0 : return false;
1651 : }
1652 :
1653 3 : JAMI_WARN("[account %s] Contact address changed: "
1654 : "(%s → %s:%d). Updating registration.",
1655 : accountID_.c_str(),
1656 : contact_addr.toString(true).c_str(),
1657 : via_addrstr.data(),
1658 : rport);
1659 :
1660 : /*
1661 : * Build new Contact header
1662 : */
1663 : {
1664 6 : auto tempContact = printContactHeader(config().username,
1665 3 : config().displayName,
1666 : via_addrstr,
1667 : rport,
1668 3 : PJSIP_TRANSPORT_IS_SECURE(tp),
1669 6 : config().deviceKey);
1670 :
1671 3 : if (tempContact.empty()) {
1672 0 : JAMI_ERR("Invalid contact header");
1673 0 : return false;
1674 : }
1675 :
1676 : // Update
1677 3 : std::lock_guard lock(contactMutex_);
1678 3 : contactHeader_ = std::move(tempContact);
1679 3 : }
1680 :
1681 3 : if (regc_ != nullptr) {
1682 3 : auto contactHdr = getContactHeader();
1683 3 : auto pjContact = sip_utils::CONST_PJ_STR(contactHdr);
1684 3 : pjsip_regc_update_contact(regc_, 1, &pjContact);
1685 :
1686 : /* Perform new registration at the next registration cycle */
1687 3 : }
1688 :
1689 3 : return true;
1690 3 : }
1691 :
1692 : /* Auto re-registration timeout callback */
1693 : void
1694 0 : SIPAccount::autoReregTimerCb()
1695 : {
1696 : /* Check if the re-registration timer is still valid, e.g: while waiting
1697 : * timeout timer application might have deleted the account or disabled
1698 : * the auto-reregistration.
1699 : */
1700 0 : if (not auto_rereg_.active)
1701 0 : return;
1702 :
1703 : /* Start re-registration */
1704 0 : ++auto_rereg_.attempt_cnt;
1705 : try {
1706 : // If attempt_count was 0, we should call doRegister to reset transports if needed.
1707 0 : if (auto_rereg_.attempt_cnt == 1)
1708 0 : doRegister();
1709 : else
1710 0 : sendRegister();
1711 0 : } catch (const VoipLinkException& e) {
1712 0 : JAMI_ERR("Exception during SIP registration: %s", e.what());
1713 0 : scheduleReregistration();
1714 0 : }
1715 : }
1716 :
1717 : /* Schedule reregistration for specified account. Note that the first
1718 : * re-registration after a registration failure will be done immediately.
1719 : * Also note that this function should be called within PJSUA mutex.
1720 : */
1721 : void
1722 0 : SIPAccount::scheduleReregistration()
1723 : {
1724 0 : if (!isUsable())
1725 0 : return;
1726 :
1727 : /* Cancel any re-registration timer */
1728 0 : if (auto_rereg_.timer.id) {
1729 0 : auto_rereg_.timer.id = PJ_FALSE;
1730 0 : pjsip_endpt_cancel_timer(link_.getEndpoint(), &auto_rereg_.timer);
1731 : }
1732 :
1733 : /* Update re-registration flag */
1734 0 : auto_rereg_.active = PJ_TRUE;
1735 :
1736 : /* Set up timer for reregistration */
1737 0 : auto_rereg_.timer.cb = [](pj_timer_heap_t* /*th*/, pj_timer_entry* te) {
1738 0 : if (auto sipAccount = static_cast<std::weak_ptr<SIPAccount>*>(te->user_data)->lock())
1739 0 : sipAccount->autoReregTimerCb();
1740 0 : };
1741 0 : if (not auto_rereg_.timer.user_data)
1742 0 : auto_rereg_.timer.user_data = new std::weak_ptr<SIPAccount>(weak());
1743 :
1744 : /* Reregistration attempt. The first attempt will be done sooner */
1745 : pj_time_val delay;
1746 0 : delay.sec = auto_rereg_.attempt_cnt ? REGISTRATION_RETRY_INTERVAL : REGISTRATION_FIRST_RETRY_INTERVAL;
1747 0 : delay.msec = 0;
1748 :
1749 : /* Randomize interval by ±10 secs */
1750 0 : if (delay.sec >= 10) {
1751 0 : delay.msec = delay10ZeroDist_(rand);
1752 : } else {
1753 0 : delay.sec = 0;
1754 0 : delay.msec = delay10PosDist_(rand);
1755 : }
1756 :
1757 0 : pj_time_val_normalize(&delay);
1758 :
1759 0 : JAMI_WARNING("Scheduling re-registration attempt in {:d} second(s)…", delay.sec);
1760 0 : auto_rereg_.timer.id = PJ_TRUE;
1761 0 : if (pjsip_endpt_schedule_timer(link_.getEndpoint(), &auto_rereg_.timer, &delay) != PJ_SUCCESS)
1762 0 : auto_rereg_.timer.id = PJ_FALSE;
1763 : }
1764 :
1765 : void
1766 10 : SIPAccount::updateDialogViaSentBy(pjsip_dialog* dlg)
1767 : {
1768 10 : if (config().allowIPAutoRewrite && via_addr_.host.slen > 0)
1769 1 : pjsip_dlg_set_via_sent_by(dlg, &via_addr_, via_tp_);
1770 10 : }
1771 :
1772 : #if 0
1773 : /**
1774 : * Create Accept header for MESSAGE.
1775 : */
1776 : static pjsip_accept_hdr* im_create_accept(pj_pool_t *pool)
1777 : {
1778 : /* Create Accept header. */
1779 : pjsip_accept_hdr *accept;
1780 :
1781 : accept = pjsip_accept_hdr_create(pool);
1782 : accept->values[0] = CONST_PJ_STR("text/plain");
1783 : accept->values[1] = CONST_PJ_STR("application/im-iscomposing+xml");
1784 : accept->count = 2;
1785 :
1786 : return accept;
1787 : }
1788 : #endif
1789 :
1790 : void
1791 0 : SIPAccount::sendMessage(const std::string& to,
1792 : const std::string&,
1793 : const std::map<std::string, std::string>& payloads,
1794 : uint64_t id,
1795 : bool,
1796 : bool)
1797 : {
1798 0 : if (to.empty() or payloads.empty()) {
1799 0 : JAMI_WARN("No sender or payload");
1800 0 : messageEngine_.onMessageSent(to, id, false);
1801 0 : return;
1802 : }
1803 :
1804 0 : auto toUri = getToUri(to);
1805 :
1806 0 : constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)};
1807 0 : std::string from(getFromUri());
1808 0 : pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
1809 0 : pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
1810 :
1811 : /* Create request. */
1812 : pjsip_tx_data* tdata;
1813 0 : pj_status_t status = pjsip_endpt_create_request(
1814 0 : link_.getEndpoint(), &msg_method, &pjTo, &pjFrom, &pjTo, nullptr, nullptr, -1, nullptr, &tdata);
1815 0 : if (status != PJ_SUCCESS) {
1816 0 : JAMI_ERROR("Unable to create request: {:s}", sip_utils::sip_strerror(status));
1817 0 : messageEngine_.onMessageSent(to, id, false);
1818 0 : return;
1819 : }
1820 :
1821 : /* Add Date Header. */
1822 : pj_str_t date_str;
1823 0 : constexpr auto key = CONST_PJ_STR("Date");
1824 : pjsip_hdr* hdr;
1825 0 : auto time = std::time(nullptr);
1826 0 : auto date = std::ctime(&time);
1827 : // the erase-remove idiom for a Cstring, removes _all_ new lines with in date
1828 0 : *std::remove(date, date + strlen(date), '\n') = '\0';
1829 :
1830 : // Add Header
1831 0 : hdr = reinterpret_cast<pjsip_hdr*>(pjsip_date_hdr_create(tdata->pool, &key, pj_cstr(&date_str, date)));
1832 0 : pjsip_msg_add_hdr(tdata->msg, hdr);
1833 :
1834 : // Add user-agent header
1835 0 : sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
1836 :
1837 : // Set input token into callback
1838 0 : std::unique_ptr<ctx> t {new ctx(new pjsip_auth_clt_sess)};
1839 0 : t->acc = shared();
1840 0 : t->to = to;
1841 0 : t->id = id;
1842 :
1843 : /* Initialize Auth header. */
1844 0 : status = pjsip_auth_clt_init(t->auth_sess.get(), link_.getEndpoint(), tdata->pool, 0);
1845 :
1846 0 : if (status != PJ_SUCCESS) {
1847 0 : JAMI_ERROR("Unable to initialize auth session: {:s}", sip_utils::sip_strerror(status));
1848 0 : messageEngine_.onMessageSent(to, id, false);
1849 0 : return;
1850 : }
1851 :
1852 0 : status = pjsip_auth_clt_set_credentials(t->auth_sess.get(), getCredentialCount(), getCredInfo());
1853 :
1854 0 : if (status != PJ_SUCCESS) {
1855 0 : JAMI_ERROR("Unable to set auth session data: {:s}", sip_utils::sip_strerror(status));
1856 0 : messageEngine_.onMessageSent(to, id, false);
1857 0 : return;
1858 : }
1859 :
1860 0 : const pjsip_tpselector tp_sel = getTransportSelector();
1861 0 : status = pjsip_tx_data_set_transport(tdata, &tp_sel);
1862 :
1863 0 : if (status != PJ_SUCCESS) {
1864 0 : JAMI_ERROR("Unable to set transport: {:s}", sip_utils::sip_strerror(status));
1865 0 : messageEngine_.onMessageSent(to, id, false);
1866 0 : return;
1867 : }
1868 :
1869 0 : im::fillPJSIPMessageBody(*tdata, payloads);
1870 :
1871 : // Send message request with callback SendMessageOnComplete
1872 0 : status = pjsip_endpt_send_request(link_.getEndpoint(), tdata, -1, t.release(), &onComplete);
1873 :
1874 0 : if (status != PJ_SUCCESS) {
1875 0 : JAMI_ERROR("Unable to send request: {:s}", sip_utils::sip_strerror(status));
1876 0 : messageEngine_.onMessageSent(to, id, false);
1877 0 : return;
1878 : }
1879 0 : }
1880 :
1881 : void
1882 0 : SIPAccount::onComplete(void* token, pjsip_event* event)
1883 : {
1884 0 : std::unique_ptr<ctx> c {(ctx*) token};
1885 : int code;
1886 : pj_status_t status;
1887 0 : pj_assert(event->type == PJSIP_EVENT_TSX_STATE);
1888 0 : code = event->body.tsx_state.tsx->status_code;
1889 :
1890 0 : auto acc = c->acc.lock();
1891 0 : if (not acc)
1892 0 : return;
1893 :
1894 : // Check if Authorization Header if needed (request rejected by server)
1895 0 : if (code == PJSIP_SC_UNAUTHORIZED || code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {
1896 0 : JAMI_INFO("Authorization needed for SMS message - Resending");
1897 : pjsip_tx_data* new_request;
1898 :
1899 : // Add Authorization Header into msg
1900 0 : status = pjsip_auth_clt_reinit_req(c->auth_sess.get(),
1901 0 : event->body.tsx_state.src.rdata,
1902 0 : event->body.tsx_state.tsx->last_tx,
1903 : &new_request);
1904 :
1905 0 : if (status == PJ_SUCCESS) {
1906 : // Increment Cseq number by one manually
1907 : pjsip_cseq_hdr* cseq_hdr;
1908 0 : cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(new_request->msg, PJSIP_H_CSEQ, NULL);
1909 0 : cseq_hdr->cseq += 1;
1910 :
1911 : // Resend request
1912 0 : auto to = c->to;
1913 0 : auto id = c->id;
1914 0 : status = pjsip_endpt_send_request(acc->link_.getEndpoint(), new_request, -1, c.release(), &onComplete);
1915 :
1916 0 : if (status != PJ_SUCCESS) {
1917 0 : JAMI_ERROR("Unable to send request: {:s}", sip_utils::sip_strerror(status));
1918 0 : acc->messageEngine_.onMessageSent(to, id, false);
1919 : }
1920 0 : return;
1921 0 : } else {
1922 0 : JAMI_ERROR("Unable to add Authorization Header into msg");
1923 0 : acc->messageEngine_.onMessageSent(c->to, c->id, false);
1924 0 : return;
1925 : }
1926 : }
1927 0 : acc->messageEngine_.onMessageSent(c->to,
1928 0 : c->id,
1929 0 : event && event->body.tsx_state.tsx
1930 0 : && (event->body.tsx_state.tsx->status_code == PJSIP_SC_OK
1931 0 : || event->body.tsx_state.tsx->status_code == PJSIP_SC_ACCEPTED));
1932 0 : }
1933 :
1934 : std::string
1935 0 : SIPAccount::getUserUri() const
1936 : {
1937 0 : return getFromUri();
1938 : }
1939 :
1940 : dhtnet::IpAddr
1941 28 : SIPAccount::createBindingAddress()
1942 : {
1943 28 : auto family = hostIp_ ? hostIp_.getFamily() : PJ_AF_INET;
1944 28 : const auto& conf = config();
1945 :
1946 28 : dhtnet::IpAddr ret = conf.bindAddress.empty()
1947 28 : ? (conf.interface == dhtnet::ip_utils::DEFAULT_INTERFACE || conf.interface.empty()
1948 28 : ? dhtnet::ip_utils::getAnyHostAddr(family)
1949 0 : : dhtnet::ip_utils::getInterfaceAddr(getLocalInterface(), family))
1950 28 : : dhtnet::IpAddr(conf.bindAddress, family);
1951 :
1952 28 : if (ret.getPort() == 0) {
1953 28 : ret.setPort(conf.tlsEnable ? conf.tlsListenerPort : conf.localPort);
1954 : }
1955 :
1956 28 : return ret;
1957 : }
1958 :
1959 : void
1960 24 : SIPAccount::setActiveCodecs(const std::vector<unsigned>& list)
1961 : {
1962 24 : Account::setActiveCodecs(list);
1963 24 : if (!hasActiveCodec(MEDIA_AUDIO)) {
1964 96 : JAMI_WARNING("All audio codecs disabled, enabling all");
1965 24 : setAllCodecsActive(MEDIA_AUDIO, true);
1966 : }
1967 24 : if (!hasActiveCodec(MEDIA_VIDEO)) {
1968 96 : JAMI_WARNING("All video codecs disabled, enabling all");
1969 24 : setAllCodecsActive(MEDIA_VIDEO, true);
1970 : }
1971 24 : config_->activeCodecs = getActiveCodecs(MEDIA_ALL);
1972 24 : }
1973 :
1974 : } // namespace jami
|