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