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