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