From edebe171393b550cd404fb94682482171b65b9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Wed, 28 Jun 2023 13:50:15 -0400 Subject: [PATCH] connectionmanager: prevent JamiAccount usage GitLab: #778 Change-Id: I5e7eb072ebda81c4ae45316cc46842dafdeaad13 --- src/connectivity/connectionmanager.cpp | 674 +++++++++++++++---- src/connectivity/connectionmanager.h | 125 +++- src/connectivity/security/diffie-hellman.cpp | 29 + src/connectivity/security/diffie-hellman.h | 3 + src/jamidht/jamiaccount.cpp | 147 ++-- src/jamidht/jamiaccount.h | 18 +- src/sip/sipvoiplink.cpp | 1 - test/unitTest/call/call.cpp | 3 +- 8 files changed, 761 insertions(+), 239 deletions(-) diff --git a/src/connectivity/connectionmanager.cpp b/src/connectivity/connectionmanager.cpp index e7ea8c066d..85afa5c084 100644 --- a/src/connectivity/connectionmanager.cpp +++ b/src/connectivity/connectionmanager.cpp @@ -16,12 +16,14 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include "connectionmanager.h" -#include "jamidht/jamiaccount.h" #include "account_const.h" -#include "jamidht/account_manager.h" #include "manager.h" #include "peer_connection.h" #include "logger.h" +#include "fileutils.h" +#include "connectivity/upnp/upnp_control.h" +#include "connectivity/sip_utils.h" +#include "connectivity/security/certstore.h" #include <asio.hpp> #include <opendht/crypto.h> @@ -61,14 +63,31 @@ struct ConnectionInfo std::unique_ptr<asio::steady_timer> waitForAnswer_ {}; }; +/** + * returns whether or not UPnP is enabled and active_ + * ie: if it is able to make port mappings + */ +bool +ConnectionManager::Config::getUPnPActive() const +{ + std::lock_guard<std::mutex> lk(upnp_mtx); + if (upnpCtrl_) + return upnpCtrl_->isReady(); + return false; +} + class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl> { public: - explicit Impl(JamiAccount& account) - : account {account} + explicit Impl(std::shared_ptr<ConnectionManager::Config> config_) + : config_ {std::move(config_)} + , rand {dht::crypto::getSeededRandomEngine<std::mt19937_64>()} {} ~Impl() {} + std::shared_ptr<dht::DhtRunner> dht() { return config_->dht_; } + const dht::crypto::Identity& identity() const { return config_->id_; } + void removeUnusedConnections(const DeviceId& deviceId = {}) { std::vector<std::shared_ptr<ConnectionInfo>> unused {}; @@ -163,6 +182,87 @@ public: void onPeerResponse(const PeerConnectionRequest& req); void onDhtConnected(const dht::crypto::PublicKey& devicePk); + const std::shared_future<tls::DhParams> dhParams() const; + tls::CertificateStore& certStore() const { return *config_->certStore_; } + + mutable std::mutex messageMutex_ {}; + std::set<std::string, std::less<>> treatedMessages_ {}; + + void loadTreatedMessages(); + void saveTreatedMessages() const; + + /// \return true if the given DHT message identifier has been treated + /// \note if message has not been treated yet this method store this id and returns true at + /// further calls + bool isMessageTreated(std::string_view id); + + /** + * Published IPv4/IPv6 addresses, used only if defined by the user in account + * configuration + * + */ + IpAddr publishedIp_[2] {}; + + // This will be stored in the configuration + std::string publishedIpAddress_ {}; + + /** + * Published port, used only if defined by the user + */ + pj_uint16_t publishedPort_ {sip_utils::DEFAULT_SIP_PORT}; + + /** + * interface name on which this account is bound + */ + std::string interface_ {"default"}; + + /** + * Get the local interface name on which this account is bound. + */ + const std::string& getLocalInterface() const { return interface_; } + + /** + * Get the published IP address, fallbacks to NAT if family is unspecified + * Prefers the usage of IPv4 if possible. + */ + IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const; + + /** + * Set published IP address according to given family + */ + void setPublishedAddress(const IpAddr& ip_addr); + + /** + * Store the local/public addresses used to register + */ + void storeActiveIpAddress(std::function<void()>&& cb = {}); + + /** + * Create and return ICE options. + */ + void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept; + IceTransportOptions getIceOptions() const noexcept; + + /** + * Inform that a potential peer device have been found. + * Returns true only if the device certificate is a valid device certificate. + * In that case (true is returned) the account_id parameter is set to the peer account ID. + */ + static bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, + dht::InfoHash& account_id); + + bool findCertificate(const dht::PkId& id, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb); + bool findCertificate( + const dht::InfoHash& h, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {}); + + /** + * returns whether or not UPnP is enabled and active + * ie: if it is able to make port mappings + */ + bool getUPnPActive() const; + /** * Triggered when a new TLS socket is ready to use * @param ok If succeed @@ -175,7 +275,11 @@ public: const dht::Value::Id& vid, const std::string& name = ""); - JamiAccount& account; + std::shared_ptr<ConnectionManager::Config> config_; + + mutable std::mt19937_64 rand; + + iOSConnectedCallback iOSConnectedCb_ {}; std::mutex infosMtx_ {}; // Note: Someone can ask multiple sockets, so to avoid any race condition, @@ -348,17 +452,16 @@ ConnectionManager::Impl::connectDeviceStartIce( value->user_type = "peer_request"; // Send connection request through DHT - JAMI_DBG() << account << "Request connection to " << deviceId; - account.dht()->putEncrypted( - dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk->getId().toString()), - devicePk, - value, - [deviceId, accId = account.getAccountID()](bool ok) { - JAMI_DEBUG("[Account {:s}] Send connection request to {:s}. Put encrypted {:s}", - accId, - deviceId.toString(), - (ok ? "ok" : "failed")); - }); + JAMI_DBG() << "Request connection to " << deviceId; + dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix + + devicePk->getId().toString()), + devicePk, + value, + [deviceId](bool ok) { + JAMI_DEBUG("Sent connection request to {:s}. Put encrypted {:s}", + deviceId.toString(), + (ok ? "ok" : "failed")); + }); // Wait for call to onResponse() operated by DHT if (isDestroying_) { onConnected(true); // This avoid to wait new negotiation when destroying @@ -404,7 +507,7 @@ ConnectionManager::Impl::onResponse(const asio::error_code& ec, auto sdp = ice->parseIceCandidates(info->response_.ice_msg); if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) { - JAMI_WARN("[Account:%s] start ICE failed", account.getAccountID().c_str()); + JAMI_WARN("start ICE failed"); info->onConnected_(false); return; } @@ -440,13 +543,12 @@ ConnectionManager::Impl::connectDeviceOnNegoDone( true); // Negotiate a TLS session - JAMI_DBG() << account - << "Start TLS session - Initied by connectDevice(). Launched by channel: " << name + JAMI_DBG() << "Start TLS session - Initied by connectDevice(). Launched by channel: " << name << " - device:" << deviceId << " - vid: " << vid; info->tls_ = std::make_unique<TlsSocketEndpoint>(std::move(endpoint), - account.certStore(), - account.identity(), - account.dhParams(), + certStore(), + identity(), + dhParams(), *cert); info->tls_->setOnReady( @@ -466,37 +568,37 @@ ConnectionManager::Impl::connectDevice(const DeviceId& deviceId, bool forceNewSocket, const std::string& connType) { - if (!account.dht()) { + if (!dht()) { cb(nullptr, deviceId); return; } - if (deviceId.toString() == account.currentDeviceId()) { + if (deviceId.toString() == identity().second->getLongId().toString()) { cb(nullptr, deviceId); return; } - account.findCertificate(deviceId, - [w = weak(), - deviceId, - name, - cb = std::move(cb), - noNewSocket, - forceNewSocket, - connType](const std::shared_ptr<dht::crypto::Certificate>& cert) { - if (!cert) { - JAMI_ERR("No valid certificate found for device %s", - deviceId.to_c_str()); - cb(nullptr, deviceId); - return; - } - if (auto shared = w.lock()) { - shared->connectDevice(cert, - name, - std::move(cb), - noNewSocket, - forceNewSocket, - connType); - } - }); + findCertificate(deviceId, + [w = weak(), + deviceId, + name, + cb = std::move(cb), + noNewSocket, + forceNewSocket, + connType](const std::shared_ptr<dht::crypto::Certificate>& cert) { + if (!cert) { + JAMI_ERR("No valid certificate found for device %s", + deviceId.to_c_str()); + cb(nullptr, deviceId); + return; + } + if (auto shared = w.lock()) { + shared->connectDevice(cert, + name, + std::move(cb), + noNewSocket, + forceNewSocket, + connType); + } + }); } void @@ -522,7 +624,7 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif cb(nullptr, deviceId); return; } - dht::Value::Id vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->account.rand); + dht::Value::Id vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->rand); auto isConnectingToDevice = false; { std::lock_guard<std::mutex> lk(sthis->connectCbsMtx_); @@ -531,7 +633,7 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif const auto& pendings = pendingsIt->second; while (pendings.connecting.find(vid) != pendings.connecting.end() && pendings.waiting.find(vid) != pendings.waiting.end()) { - vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->account.rand); + vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->rand); } } // Check if already connecting @@ -580,14 +682,14 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif }; // If no socket exists, we need to initiate an ICE connection. - sthis->account.getIceOptions([w, - deviceId = std::move(deviceId), - devicePk = std::move(devicePk), - name = std::move(name), - cert = std::move(cert), - vid, - connType, - eraseInfo](auto&& ice_config) { + sthis->getIceOptions([w, + deviceId = std::move(deviceId), + devicePk = std::move(devicePk), + name = std::move(name), + cert = std::move(cert), + vid, + connType, + eraseInfo](auto&& ice_config) { auto sthis = w.lock(); if (!sthis) { dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); }); @@ -623,11 +725,11 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif }); }; ice_config.onNegoDone = [w, - deviceId = std::move(deviceId), - name = std::move(name), - cert = std::move(cert), - vid, - eraseInfo](bool ok) { + deviceId, + name, + cert = std::move(cert), + vid, + eraseInfo](bool ok) { dht::ThreadPool::io().run([w = std::move(w), deviceId = std::move(deviceId), name = std::move(name), @@ -650,10 +752,9 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif } std::unique_lock<std::mutex> lk {info->mutex_}; ice_config.master = false; - ice_config.streamsCount = JamiAccount::ICE_STREAMS_COUNT; - ice_config.compCountPerStream = JamiAccount::ICE_COMP_COUNT_PER_STREAM; - info->ice_ = Manager::instance().getIceTransportFactory().createUTransport( - sthis->account.getAccountID()); + ice_config.streamsCount = 1; + ice_config.compCountPerStream = 1; + info->ice_ = Manager::instance().getIceTransportFactory().createUTransport(""); if (!info->ice_) { JAMI_ERR("Cannot initialize ICE session."); eraseInfo(); @@ -715,7 +816,7 @@ void ConnectionManager::Impl::onPeerResponse(const PeerConnectionRequest& req) { auto device = req.owner->getLongId(); - JAMI_INFO() << account << " New response received from " << device.to_c_str(); + JAMI_INFO() << "New response received from " << device.to_c_str(); if (auto info = getInfo(device, req.id)) { std::lock_guard<std::mutex> lk {info->mutex_}; info->responseReceived_ = true; @@ -727,26 +828,24 @@ ConnectionManager::Impl::onPeerResponse(const PeerConnectionRequest& req) device, req.id)); } else { - JAMI_WARN() << account << " respond received, but cannot find request"; + JAMI_WARN() << "Respond received, but cannot find request"; } } void ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk) { - if (!account.dht()) { + if (!dht()) return; - } - account.dht()->listen<PeerConnectionRequest>( + dht()->listen<PeerConnectionRequest>( dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()), [w = weak()](PeerConnectionRequest&& req) { auto shared = w.lock(); if (!shared) return false; - if (shared->account.isMessageTreated(to_hex_string(req.id))) { - // Message already treated. Just ignore + // Message already treated. Just ignore + if (shared->isMessageTreated(to_hex_string(req.id))) return true; - } if (req.isAnswer) { JAMI_DBG() << "Received request answer from " << req.owner->getLongId(); } else { @@ -756,7 +855,7 @@ ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk) shared->onPeerResponse(req); } else { // Async certificate checking - shared->account.findCertificate( + shared->findCertificate( req.from, [w, req = std::move(req)]( const std::shared_ptr<dht::crypto::Certificate>& cert) mutable { @@ -764,21 +863,15 @@ ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk) if (!shared) return; dht::InfoHash peer_h; - if (AccountManager::foundPeerDevice(cert, peer_h)) { + if (foundPeerDevice(cert, peer_h)) { #if TARGET_OS_IOS - if ((req.connType == "videoCall" || req.connType == "audioCall") - && jami::Manager::instance().isIOSExtension) { - bool hasVideo = req.connType == "videoCall"; - emitSignal<libjami::ConversationSignal::CallConnectionRequest>( - shared->account.getAccountID(), peer_h.toString(), hasVideo); + if (shared->iOSConnectedCb_(req.connType, peer_h)) return; - } #endif shared->onDhtPeerRequest(req, cert); } else { - JAMI_WARN() - << shared->account << "Rejected untrusted connection request from " - << req.owner->getLongId(); + JAMI_WARN() << "Rejected untrusted connection request from " + << req.owner->getLongId(); } }); } @@ -860,17 +953,16 @@ ConnectionManager::Impl::answerTo(IceTransport& ice, auto value = std::make_shared<dht::Value>(std::move(val)); value->user_type = "peer_request"; - JAMI_DBG() << account << "[CNX] connection accepted, DHT reply to " << from->getLongId(); - account.dht()->putEncrypted( - dht::InfoHash::get(PeerConnectionRequest::key_prefix + from->getId().toString()), - from, - value, - [from, accId = account.getAccountID()](bool ok) { - JAMI_DEBUG("[Account {:s}] Answer to connection request from {:s}. Put encrypted {:s}", - accId, - from->getLongId().toString(), - (ok ? "ok" : "failed")); - }); + JAMI_DBG() << "[CNX] connection accepted, DHT reply to " << from->getLongId(); + dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix + + from->getId().toString()), + from, + value, + [from](bool ok) { + JAMI_DEBUG("Answer to connection request from {:s}. Put encrypted {:s}", + from->getLongId().toString(), + (ok ? "ok" : "failed")); + }); } bool @@ -893,7 +985,7 @@ ConnectionManager::Impl::onRequestStartIce(const PeerConnectionRequest& req) auto sdp = ice->parseIceCandidates(req.ice_msg); answerTo(*ice, req.id, req.owner); if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) { - JAMI_ERR("[Account:%s] start ICE failed - fallback to TURN", account.getAccountID().c_str()); + JAMI_ERR("Start ICE failed - fallback to TURN"); ice = nullptr; if (connReadyCb_) connReadyCb_(deviceId, "", nullptr); @@ -924,18 +1016,18 @@ ConnectionManager::Impl::onRequestOnNegoDone(const PeerConnectionRequest& req) // init TLS session auto ph = req.from; - JAMI_DBG() << account << "Start TLS session - Initied by DHT request. Device:" << req.from + JAMI_DBG() << "Start TLS session - Initied by DHT request. Device:" << req.from << " - vid: " << req.id; info->tls_ = std::make_unique<TlsSocketEndpoint>( std::move(endpoint), - account.certStore(), - account.identity(), - account.dhParams(), + certStore(), + identity(), + dhParams(), [ph, w = weak()](const dht::crypto::Certificate& cert) { auto shared = w.lock(); if (!shared) return false; - auto crt = shared->account.certStore().getCertificate(cert.getLongId().toString()); + auto crt = shared->certStore().getCertificate(cert.getLongId().toString()); if (!crt) return false; return crt->getPacked() == cert.getPacked(); @@ -954,16 +1046,14 @@ ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req, const std::shared_ptr<dht::crypto::Certificate>& /*cert*/) { auto deviceId = req.owner->getLongId(); - JAMI_INFO() << account << "New connection requested by " << deviceId; + JAMI_INFO() << "New connection requested by " << deviceId; if (!iceReqCb_ || !iceReqCb_(deviceId)) { - JAMI_INFO("[Account:%s] refuse connection from %s", - account.getAccountID().c_str(), - deviceId.toString().c_str()); + JAMI_INFO("Refuse connection from %s", deviceId.toString().c_str()); return; } // Because the connection is accepted, create an ICE socket. - account.getIceOptions([w = weak(), req, deviceId](auto&& ice_config) { + getIceOptions([w = weak(), req, deviceId](auto&& ice_config) { auto shared = w.lock(); if (!shared) return; @@ -1025,15 +1115,12 @@ ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req, std::lock_guard<std::mutex> lk(shared->infosMtx_); shared->infos_[{deviceId, req.id}] = info; } - JAMI_INFO("[Account:%s] accepting connection from %s", - shared->account.getAccountID().c_str(), - deviceId.toString().c_str()); + JAMI_INFO("accepting connection from %s", deviceId.toString().c_str()); std::unique_lock<std::mutex> lk {info->mutex_}; - ice_config.streamsCount = JamiAccount::ICE_STREAMS_COUNT; - ice_config.compCountPerStream = JamiAccount::ICE_COMP_COUNT_PER_STREAM; + ice_config.streamsCount = 1; + ice_config.compCountPerStream = 1; // TCP ice_config.master = true; - info->ice_ = Manager::instance().getIceTransportFactory().createUTransport( - shared->account.getAccountID()); + info->ice_ = Manager::instance().getIceTransportFactory().createUTransport(""); if (not info->ice_) { JAMI_ERR("Cannot initialize ICE session."); eraseInfo(); @@ -1094,8 +1181,296 @@ ConnectionManager::Impl::addNewMultiplexedSocket(const CallbackId& id, const std }); } -ConnectionManager::ConnectionManager(JamiAccount& account) - : pimpl_ {std::make_shared<Impl>(account)} +const std::shared_future<tls::DhParams> +ConnectionManager::Impl::dhParams() const +{ + return dht::ThreadPool::computation().get<tls::DhParams>( + std::bind(tls::DhParams::loadDhParams, fileutils::get_cache_dir() + DIR_SEPARATOR_STR "dhParams")); + ; +} + +template<typename ID = dht::Value::Id> +std::set<ID, std::less<>> +loadIdList(const std::string& path) +{ + std::set<ID, std::less<>> ids; + std::ifstream file = fileutils::ifstream(path); + if (!file.is_open()) { + JAMI_DBG("Could not load %s", path.c_str()); + return ids; + } + std::string line; + while (std::getline(file, line)) { + if constexpr (std::is_same<ID, std::string>::value) { + ids.emplace(std::move(line)); + } else if constexpr (std::is_integral<ID>::value) { + ID vid; + if (auto [p, ec] = std::from_chars(line.data(), line.data() + line.size(), vid, 16); + ec == std::errc()) { + ids.emplace(vid); + } + } + } + return ids; +} + +template<typename List = std::set<dht::Value::Id>> +void +saveIdList(const std::string& path, const List& ids) +{ + std::ofstream file = fileutils::ofstream(path, std::ios::trunc | std::ios::binary); + if (!file.is_open()) { + JAMI_ERR("Could not save to %s", path.c_str()); + return; + } + for (auto& c : ids) + file << std::hex << c << "\n"; +} + +void +ConnectionManager::Impl::loadTreatedMessages() +{ + std::lock_guard<std::mutex> lock(messageMutex_); + auto path = config_->cachePath + DIR_SEPARATOR_STR "treatedMessages"; + treatedMessages_ = loadIdList<std::string>(path); + if (treatedMessages_.empty()) { + auto messages = loadIdList(path); + for (const auto& m : messages) + treatedMessages_.emplace(to_hex_string(m)); + } +} + +void +ConnectionManager::Impl::saveTreatedMessages() const +{ + dht::ThreadPool::io().run([w = weak()]() { + if (auto sthis = w.lock()) { + auto& this_ = *sthis; + std::lock_guard<std::mutex> lock(this_.messageMutex_); + fileutils::check_dir(this_.config_->cachePath.c_str()); + saveIdList<decltype(this_.treatedMessages_)>(this_.config_->cachePath + + DIR_SEPARATOR_STR "treatedMessages", + this_.treatedMessages_); + } + }); +} + +bool +ConnectionManager::Impl::isMessageTreated(std::string_view id) +{ + std::lock_guard<std::mutex> lock(messageMutex_); + auto res = treatedMessages_.emplace(id); + if (res.second) { + saveTreatedMessages(); + return false; + } + return true; +} + +/** + * returns whether or not UPnP is enabled and active_ + * ie: if it is able to make port mappings + */ +bool +ConnectionManager::Impl::getUPnPActive() const +{ + return config_->getUPnPActive(); +} + +IpAddr +ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const +{ + if (family == AF_INET) + return publishedIp_[0]; + if (family == AF_INET6) + return publishedIp_[1]; + + assert(family == AF_UNSPEC); + + // If family is not set, prefere IPv4 if available. It's more + // likely to succeed behind NAT. + if (publishedIp_[0]) + return publishedIp_[0]; + if (publishedIp_[1]) + return publishedIp_[1]; + return {}; +} + +void +ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr) +{ + if (ip_addr.getFamily() == AF_INET) { + publishedIp_[0] = ip_addr; + } else { + publishedIp_[1] = ip_addr; + } +} + +void +ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb) +{ + dht()->getPublicAddress([this, cb = std::move(cb)](std::vector<dht::SockAddr>&& results) { + bool hasIpv4 {false}, hasIpv6 {false}; + for (auto& result : results) { + auto family = result.getFamily(); + if (family == AF_INET) { + if (not hasIpv4) { + hasIpv4 = true; + JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str()); + setPublishedAddress(*result.get()); + if (config_->upnpCtrl_) { + config_->upnpCtrl_->setPublicAddress(*result.get()); + } + } + } else if (family == AF_INET6) { + if (not hasIpv6) { + hasIpv6 = true; + JAMI_DBG("Store DHT public IPv6 address : %s", result.toString().c_str()); + setPublishedAddress(*result.get()); + } + } + if (hasIpv4 and hasIpv6) + break; + } + if (cb) + cb(); + }); +} + +void +ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept +{ + storeActiveIpAddress([this, cb = std::move(cb)] { + IceTransportOptions opts = ConnectionManager::Impl::getIceOptions(); + auto publishedAddr = getPublishedIpAddress(); + + if (publishedAddr) { + auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(), + publishedAddr.getFamily()); + if (interfaceAddr) { + opts.accountLocalAddr = interfaceAddr; + opts.accountPublicAddr = publishedAddr; + } + } + if (cb) + cb(std::move(opts)); + }); +} + +IceTransportOptions +ConnectionManager::Impl::getIceOptions() const noexcept +{ + IceTransportOptions opts; + opts.upnpEnable = getUPnPActive(); + + if (config_->stunEnabled_) + opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer_)); + if (config_->turnEnabled_) { + auto cached = false; + std::lock_guard<std::mutex> lk(config_->cachedTurnMutex_); + cached = config_->cacheTurnV4_ || config_->cacheTurnV6_; + if (config_->cacheTurnV4_ && *config_->cacheTurnV4_) { + opts.turnServers.emplace_back(TurnServerInfo() + .setUri(config_->cacheTurnV4_->toString(true)) + .setUsername(config_->turnServerUserName_) + .setPassword(config_->turnServerPwd_) + .setRealm(config_->turnServerRealm_)); + } + // NOTE: first test with ipv6 turn was not concluant and resulted in multiple + // co issues. So this needs some debug. for now just disable + // if (cacheTurnV6_ && *cacheTurnV6_) { + // opts.turnServers.emplace_back(TurnServerInfo() + // .setUri(cacheTurnV6_->toString(true)) + // .setUsername(turnServerUserName_) + // .setPassword(turnServerPwd_) + // .setRealm(turnServerRealm_)); + //} + // Nothing cached, so do the resolution + if (!cached) { + opts.turnServers.emplace_back(TurnServerInfo() + .setUri(config_->turnServer_) + .setUsername(config_->turnServerUserName_) + .setPassword(config_->turnServerPwd_) + .setRealm(config_->turnServerRealm_)); + } + } + return opts; +} + +bool +ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, + dht::InfoHash& account_id) +{ + if (not crt) + return false; + + auto top_issuer = crt; + while (top_issuer->issuer) + top_issuer = top_issuer->issuer; + + // Device certificate can't be self-signed + if (top_issuer == crt) { + JAMI_WARN("Found invalid peer device: %s", crt->getLongId().toString().c_str()); + return false; + } + + // Check peer certificate chain + // Trust store with top issuer as the only CA + dht::crypto::TrustList peer_trust; + peer_trust.add(*top_issuer); + if (not peer_trust.verify(*crt)) { + JAMI_WARN("Found invalid peer device: %s", crt->getLongId().toString().c_str()); + return false; + } + + // Check cached OCSP response + if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) { + JAMI_ERR("Certificate %s is disabled by cached OCSP response", crt->getLongId().to_c_str()); + return false; + } + + account_id = crt->issuer->getId(); + JAMI_WARN("Found peer device: %s account:%s CA:%s", + crt->getLongId().toString().c_str(), + account_id.toString().c_str(), + top_issuer->getId().toString().c_str()); + return true; +} + +bool +ConnectionManager::Impl::findCertificate( + const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) +{ + if (auto cert = certStore().getCertificate(id.toString())) { + if (cb) + cb(cert); + } else if (cb) + cb(nullptr); + return true; +} + +bool +ConnectionManager::Impl::findCertificate(const dht::InfoHash& h, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) +{ + if (auto cert = certStore().getCertificate(h.toString())) { + if (cb) + cb(cert); + } else { + dht()->findCertificate(h, + [cb = std::move(cb), this]( + const std::shared_ptr<dht::crypto::Certificate>& crt) { + if (crt) + certStore().pinCertificate(crt); + if (cb) + cb(crt); + }); + } + return true; +} + +ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_) + : pimpl_ {std::make_shared<Impl>(config_)} {} ConnectionManager::~ConnectionManager() @@ -1144,7 +1519,7 @@ ConnectionManager::closeConnectionsWith(const std::string& peerUri) for (auto iter = pimpl_->infos_.begin(); iter != pimpl_->infos_.end();) { auto const& [key, value] = *iter; auto deviceId = key.first; - auto cert = pimpl_->account.certStore().getCertificate(deviceId.toString()); + auto cert = pimpl_->certStore().getCertificate(deviceId.toString()); if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) { connInfos.emplace_back(value); peersDevices.emplace(deviceId); @@ -1197,6 +1572,12 @@ ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb) pimpl_->connReadyCb_ = std::move(cb); } +void +ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb) +{ + pimpl_->iOSConnectedCb_ = std::move(cb); +} + std::size_t ConnectionManager::activeSockets() const { @@ -1208,16 +1589,12 @@ void ConnectionManager::monitor() const { std::lock_guard<std::mutex> lk(pimpl_->infosMtx_); - JAMI_DBG("ConnectionManager for account %s (%s), current status:", - pimpl_->account.getAccountID().c_str(), - pimpl_->account.getUserUri().c_str()); + JAMI_DBG("ConnectionManager current status:"); for (const auto& [_, ci] : pimpl_->infos_) { if (ci->socket_) ci->socket_->monitor(); } - JAMI_DBG("ConnectionManager for account %s (%s), end status.", - pimpl_->account.getAccountID().c_str(), - pimpl_->account.getUserUri().c_str()); + JAMI_DBG("ConnectionManager end status."); } void @@ -1230,4 +1607,55 @@ ConnectionManager::connectivityChanged() } } +void +ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept +{ + pimpl_->getIceOptions(std::move(cb)); +} + +IceTransportOptions +ConnectionManager::getIceOptions() const noexcept +{ + return pimpl_->getIceOptions(); +} + +IpAddr +ConnectionManager::getPublishedIpAddress(uint16_t family) const +{ + return pimpl_->getPublishedIpAddress(family); +} + +void +ConnectionManager::setPublishedAddress(const IpAddr& ip_addr) +{ + pimpl_->setPublishedAddress(ip_addr); +} + +void +ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb) +{ + pimpl_->storeActiveIpAddress(std::move(cb)); +} + +std::shared_ptr<ConnectionManager::Config> +ConnectionManager::getConfig() +{ + return pimpl_->config_; +} + +bool +ConnectionManager::findCertificate(const dht::PkId& id, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) +{ + return pimpl_->findCertificate(id, std::move(cb)); +} + +bool +ConnectionManager::findCertificate( + const dht::InfoHash& h, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) +{ + return pimpl_->findCertificate(h, std::move(cb)); +} + } // namespace jami diff --git a/src/connectivity/connectionmanager.h b/src/connectivity/connectionmanager.h index b81b8390af..79f9fb3d22 100644 --- a/src/connectivity/connectionmanager.h +++ b/src/connectivity/connectionmanager.h @@ -27,10 +27,11 @@ #include <opendht/default_types.h> #include "multiplexed_socket.h" +#include "connectivity/security/diffie-hellman.h" +#include "connectivity/upnp/upnp_control.h" namespace jami { -class JamiAccount; class ChannelSocket; class ConnectionManager; @@ -69,6 +70,9 @@ using ConnectCallback = std::function<void(const std::shared_ptr<ChannelSocket>& using ConnectionReadyCallback = std::function< void(const DeviceId&, const std::string& /* channel_name */, std::shared_ptr<ChannelSocket>)>; +using iOSConnectedCallback + = std::function<bool(const std::string& /* connType */, dht::InfoHash /* peer_h */)>; + /** * Manages connections to other devices * @note the account MUST be valid if ConnectionManager lives @@ -76,7 +80,9 @@ using ConnectionReadyCallback = std::function< class ConnectionManager { public: - ConnectionManager(JamiAccount& account); + class Config; + + ConnectionManager(std::shared_ptr<Config> config_); ~ConnectionManager(); /** @@ -145,6 +151,12 @@ public: */ void onConnectionReady(ConnectionReadyCallback&& cb); + /** + * Trigger cb when connection with peer is ready for iOS devices + * @param cb Callback to trigger + */ + void oniOSConnected(iOSConnectedCallback&& cb); + /** * @return the number of active sockets */ @@ -160,10 +172,119 @@ public: */ void connectivityChanged(); + /** + * Create and return ICE options. + */ + void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept; + IceTransportOptions getIceOptions() const noexcept; + + /** + * Get the published IP address, fallbacks to NAT if family is unspecified + * Prefers the usage of IPv4 if possible. + */ + IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const; + + /** + * Set published IP address according to given family + */ + void setPublishedAddress(const IpAddr& ip_addr); + + /** + * Store the local/public addresses used to register + */ + void storeActiveIpAddress(std::function<void()>&& cb = {}); + + std::shared_ptr<Config> getConfig(); + + bool findCertificate(const dht::PkId& id, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb); + bool findCertificate( + const dht::InfoHash& h, + std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {}); + private: ConnectionManager() = delete; class Impl; std::shared_ptr<Impl> pimpl_; }; +class ConnectionManager::Config : public std::enable_shared_from_this<ConnectionManager::Config> +{ +public: + explicit Config(std::shared_ptr<dht::DhtRunner> dht_, + const dht::crypto::Identity& id_, + tls::CertificateStore& certStore_, + std::shared_ptr<jami::upnp::Controller> upnpCtrl_, + std::string turnServer_, + std::string turnServerUserName_, + std::string turnServerPwd_, + std::string turnServerRealm_, + bool turnEnabled_) + : dht_ {std::move(dht_)} + , id_ {id_} + , upnpCtrl_ {std::move(upnpCtrl_)} + , turnServer_ {std::move(turnServer_)} + , turnServerUserName_ {std::move(turnServerUserName_)} + , turnServerPwd_ {std::move(turnServerPwd_)} + , turnServerRealm_ {std::move(turnServerRealm_)} + , turnEnabled_ {turnEnabled_} + , certStore_(&certStore_) + {} + + ~Config() {} + + /** + * Determine if STUN public address resolution is required to register this account. In this + * case a STUN server hostname must be specified. + */ + bool stunEnabled_ {false}; + + /** + * The STUN server hostname (optional), used to provide the public IP address in case the + * softphone stay behind a NAT. + */ + std::string stunServer_ {}; + + /** + * Determine if TURN public address resolution is required to register this account. In this + * case a TURN server hostname must be specified. + */ + bool turnEnabled_ {false}; + + /** + * The TURN server hostname (optional), used to provide the public IP address in case the + * softphone stay behind a NAT. + */ + std::string turnServer_; + std::string turnServerUserName_; + std::string turnServerPwd_; + std::string turnServerRealm_; + + mutable std::mutex cachedTurnMutex_ {}; + std::unique_ptr<IpAddr> cacheTurnV4_ {}; + std::unique_ptr<IpAddr> cacheTurnV6_ {}; + + std::string cachePath {}; + + std::shared_ptr<dht::DhtRunner> dht_; + const dht::crypto::Identity& id_; + + const dht::crypto::Identity& identity() const { return id_; } + + tls::CertificateStore* certStore_; + + /** + * UPnP IGD controller and the mutex to access it + */ + bool upnpEnabled_; + mutable std::mutex upnp_mtx {}; + std::shared_ptr<jami::upnp::Controller> upnpCtrl_; + + /** + * returns whether or not UPnP is enabled and active + * ie: if it is able to make port mappings + */ + bool getUPnPActive() const; +}; + } // namespace jami \ No newline at end of file diff --git a/src/connectivity/security/diffie-hellman.cpp b/src/connectivity/security/diffie-hellman.cpp index 47f5f123c6..bc0a854ec7 100644 --- a/src/connectivity/security/diffie-hellman.cpp +++ b/src/connectivity/security/diffie-hellman.cpp @@ -20,6 +20,7 @@ #include "diffie-hellman.h" #include "logger.h" +#include "fileutils.h" #include <chrono> #include <ciso646> @@ -106,5 +107,33 @@ DhParams::generate() return params; } +DhParams +DhParams::loadDhParams(const std::string& path) +{ + std::lock_guard<std::mutex> l(fileutils::getFileLock(path)); + try { + // writeTime throw exception if file doesn't exist + auto duration = std::chrono::system_clock::now() - fileutils::writeTime(path); + if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days + throw std::runtime_error("file too old"); + + JAMI_DBG("Loading DhParams from file '%s'", path.c_str()); + return {fileutils::loadFile(path)}; + } catch (const std::exception& e) { + JAMI_DBG("Failed to load DhParams file '%s': %s", path.c_str(), e.what()); + if (auto params = tls::DhParams::generate()) { + try { + fileutils::saveFile(path, params.serialize(), 0600); + JAMI_DBG("Saved DhParams to file '%s'", path.c_str()); + } catch (const std::exception& ex) { + JAMI_WARN("Failed to save DhParams in file '%s': %s", path.c_str(), ex.what()); + } + return params; + } + JAMI_ERR("Can't generate DH params."); + return {}; + } +} + } // namespace tls } // namespace jami diff --git a/src/connectivity/security/diffie-hellman.h b/src/connectivity/security/diffie-hellman.h index 9e8f487c2f..c38bce579c 100644 --- a/src/connectivity/security/diffie-hellman.h +++ b/src/connectivity/security/diffie-hellman.h @@ -25,6 +25,7 @@ #include <vector> #include <memory> #include <cstdint> +#include <string> namespace jami { namespace tls { @@ -60,6 +61,8 @@ public: static DhParams generate(); + static DhParams loadDhParams(const std::string& path); + private: std::unique_ptr<gnutls_dh_params_int, decltype(gnutls_dh_params_deinit)*> params_ {nullptr, gnutls_dh_params_deinit}; diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index b62cc26536..350c8f84e8 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -381,7 +381,7 @@ JamiAccount::newOutgoingCall(std::string_view toUrl, const std::vector<libjami:: return {}; auto uri = Uri(toUrl); - getIceOptions([call, w = weak(), uri = std::move(uri)](auto&& opts) { + connectionManager_->getIceOptions([call, w = weak(), uri = std::move(uri)](auto&& opts) { if (call->isIceEnabled()) { if (not call->createIceMediaTransport(false) or not call->initIceMediaTransport(true, std::forward<IceTransportOptions>(opts))) { @@ -752,8 +752,9 @@ JamiAccount::onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call, const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), target.getFamily()); - IpAddr addrSdp = getPublishedSameasLocal() ? localAddress - : getPublishedIpAddress(target.getFamily()); + IpAddr addrSdp = getPublishedSameasLocal() + ? localAddress + : connectionManager_->getPublishedIpAddress(target.getFamily()); // fallback on local address if (not addrSdp) @@ -1928,7 +1929,7 @@ JamiAccount::doRegister_() connectionManager_->onICERequest([this](const DeviceId& deviceId) { std::promise<bool> accept; std::future<bool> fut = accept.get_future(); - accountManager_->findCertificate( + connectionManager_->findCertificate( deviceId, [this, &accept](const std::shared_ptr<dht::crypto::Certificate>& cert) { dht::InfoHash peer_account_id; auto res = accountManager_->onPeerCertificate(cert, @@ -2336,7 +2337,8 @@ JamiAccount::setRegistrationState(RegistrationState state, if (state == RegistrationState::REGISTERED) { JAMI_WARNING("[Account {}] connected", getAccountID()); turnCache_->refresh(); - storeActiveIpAddress(); + if (connectionManager_) + connectionManager_->storeActiveIpAddress(); } else if (state == RegistrationState::TRYING) { JAMI_WARNING("[Account {}] connecting…", getAccountID()); } else { @@ -2362,11 +2364,12 @@ JamiAccount::connectivityChanged() dht_->connectivityChanged(); { std::lock_guard<std::mutex> lkCM(connManagerMtx_); - if (connectionManager_) + if (connectionManager_) { connectionManager_->connectivityChanged(); + // reset cache + connectionManager_->setPublishedAddress({}); + } } - // reset cache - setPublishedAddress({}); } bool @@ -2374,8 +2377,8 @@ JamiAccount::findCertificate( const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) { - if (accountManager_) - return accountManager_->findCertificate(h, std::move(cb)); + if (connectionManager_) + return connectionManager_->findCertificate(h, std::move(cb)); return false; } @@ -2383,16 +2386,16 @@ bool JamiAccount::findCertificate( const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) { - if (accountManager_) - return accountManager_->findCertificate(id, std::move(cb)); + if (connectionManager_) + return connectionManager_->findCertificate(id, std::move(cb)); return false; } bool JamiAccount::findCertificate(const std::string& crt_id) { - if (accountManager_) - return accountManager_->findCertificate(dht::InfoHash(crt_id)); + if (connectionManager_) + return connectionManager_->findCertificate(dht::InfoHash(crt_id)); return false; } @@ -2525,34 +2528,6 @@ JamiAccount::getKnownDevices() const return ids; } -tls::DhParams -JamiAccount::loadDhParams(std::string path) -{ - std::lock_guard<std::mutex> l(fileutils::getFileLock(path)); - try { - // writeTime throw exception if file doesn't exist - auto duration = clock::now() - fileutils::writeTime(path); - if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days - throw std::runtime_error("file too old"); - - JAMI_DBG("Loading DhParams from file '%s'", path.c_str()); - return {fileutils::loadFile(path)}; - } catch (const std::exception& e) { - JAMI_DBG("Failed to load DhParams file '%s': %s", path.c_str(), e.what()); - if (auto params = tls::DhParams::generate()) { - try { - fileutils::saveFile(path, params.serialize(), 0600); - JAMI_DBG("Saved DhParams to file '%s'", path.c_str()); - } catch (const std::exception& ex) { - JAMI_WARN("Failed to save DhParams in file '%s': %s", path.c_str(), ex.what()); - } - return params; - } - JAMI_ERR("Can't generate DH params."); - return {}; - } -} - void JamiAccount::loadCachedUrl(const std::string& url, const std::string& cachePath, @@ -2687,7 +2662,7 @@ JamiAccount::generateDhParams() // make sure cachePath_ is writable fileutils::check_dir(cachePath_.c_str(), 0700); dhParams_ = dht::ThreadPool::computation().get<tls::DhParams>( - std::bind(loadDhParams, cachePath_ + DIR_SEPARATOR_STR "dhParams")); + std::bind(tls::DhParams::loadDhParams, cachePath_ + DIR_SEPARATOR_STR "dhParams")); } MatchRank @@ -3332,59 +3307,10 @@ JamiAccount::onIsComposing(const std::string& conversationId, } } -void -JamiAccount::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept -{ - storeActiveIpAddress([this, cb = std::move(cb)] { - auto opts = SIPAccountBase::getIceOptions(); - auto publishedAddr = getPublishedIpAddress(); - - if (publishedAddr) { - auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(), - publishedAddr.getFamily()); - if (interfaceAddr) { - opts.accountLocalAddr = interfaceAddr; - opts.accountPublicAddr = publishedAddr; - } - } - if (cb) - cb(std::move(opts)); - }); -} - -void -JamiAccount::storeActiveIpAddress(std::function<void()>&& cb) -{ - dht_->getPublicAddress([this, cb = std::move(cb)](std::vector<dht::SockAddr>&& results) { - bool hasIpv4 {false}, hasIpv6 {false}; - for (auto& result : results) { - auto family = result.getFamily(); - if (family == AF_INET) { - if (not hasIpv4) { - hasIpv4 = true; - JAMI_DBG("[Account %s] Store DHT public IPv4 address : %s", - getAccountID().c_str(), - result.toString().c_str()); - setPublishedAddress(*result.get()); - if (upnpCtrl_) { - upnpCtrl_->setPublicAddress(*result.get()); - } - } - } else if (family == AF_INET6) { - if (not hasIpv6) { - hasIpv6 = true; - JAMI_DBG("[Account %s] Store DHT public IPv6 address : %s", - getAccountID().c_str(), - result.toString().c_str()); - setPublishedAddress(*result.get()); - } - } - if (hasIpv4 and hasIpv6) - break; - } - if (cb) - cb(); - }); +IceTransportOptions +JamiAccount::getIceOptions() const noexcept +{ + return connectionManager_->getIceOptions(); } bool @@ -4255,15 +4181,38 @@ void JamiAccount::initConnectionManager() { if (!connectionManager_) { - connectionManager_ = std::make_unique<ConnectionManager>(*this); - channelHandlers_[Uri::Scheme::SWARM] - = std::make_unique<SwarmChannelHandler>(shared(), *connectionManager_.get()); + auto connectionManagerConfig + = std::make_shared<ConnectionManager::Config>(dht(), + identity(), + certStore(), + upnpCtrl_, + config().turnServer, + config().turnServerUserName, + config().turnServerPwd, + config().turnServerRealm, + config().turnEnabled); + connectionManagerConfig->cachePath = cachePath_; + connectionManager_ = std::make_unique<ConnectionManager>(connectionManagerConfig); channelHandlers_[Uri::Scheme::GIT] = std::make_unique<ConversationChannelHandler>(shared(), *connectionManager_.get()); channelHandlers_[Uri::Scheme::SYNC] = std::make_unique<SyncChannelHandler>(shared(), *connectionManager_.get()); channelHandlers_[Uri::Scheme::DATA_TRANSFER] = std::make_unique<TransferChannelHandler>(shared(), *connectionManager_.get()); + +#if TARGET_OS_IOS + connectionManager_->oniOSConnected([&](const std::string& connType, dht::InfoHash peer_h) { + if ((connType == "videoCall" || connType == "audioCall") + && jami::Manager::instance().isIOSExtension) { + bool hasVideo = connType == "videoCall"; + emitSignal<libjami::ConversationSignal::CallConnectionRequest>("", + peer_h.toString(), + hasVideo); + return true; + } + return false; + }); +#endif } } diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 18fcdcf509..93dbf72744 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -329,6 +329,11 @@ public: const std::map<std::string, std::string>& msg); void onIsComposing(const std::string& conversationId, const std::string& peer, bool isWriting); + /** + * Create and return ICE options. + */ + IceTransportOptions getIceOptions() const noexcept; + /* Devices */ void addDevice(const std::string& password); /** @@ -423,16 +428,6 @@ public: */ std::map<std::string, std::string> getNearbyPeers() const override; - /** - * Store the local/public addresses used to register - */ - void storeActiveIpAddress(std::function<void()>&& cb = {}); - - /** - * Create and return ICE options. - */ - void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept; - #ifdef LIBJAMI_TESTABLE ConnectionManager& connectionManager() { @@ -693,8 +688,6 @@ private: const std::shared_ptr<dht::crypto::Certificate>& from_cert, const dht::InfoHash& from); - static tls::DhParams loadDhParams(std::string path); - void loadCachedUrl(const std::string& url, const std::string& cachePath, const std::chrono::seconds& cacheDuration, @@ -792,7 +785,6 @@ private: */ // TODO move in separate class void testTurn(IpAddr server); - void cacheTurnServers(); std::unique_ptr<TurnTransport> testTurnV4_; std::unique_ptr<TurnTransport> testTurnV6_; void refreshTurnDelay(bool scheduleNext); diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp index 588a859d54..7f8149a5db 100644 --- a/src/sip/sipvoiplink.cpp +++ b/src/sip/sipvoiplink.cpp @@ -411,7 +411,6 @@ transaction_request_cb(pjsip_rx_data* rdata) auto call = account->newIncomingCall(std::string(remote_user), MediaAttribute::mediaAttributesToMediaMaps(localMediaList), transport); - if (!call) { return PJ_FALSE; } diff --git a/test/unitTest/call/call.cpp b/test/unitTest/call/call.cpp index 2e0ca02958..139e8c4bcc 100644 --- a/test/unitTest/call/call.cpp +++ b/test/unitTest/call/call.cpp @@ -297,9 +297,10 @@ CallTest::testDeclineMultiDevice() JAMI_INFO("Start call between alice and Bob"); auto bobAccount2 = Manager::instance().getAccount<JamiAccount>(bob2Id); + auto call = libjami::placeCallWithMedia(aliceId, bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callReceived == 2 && !callIdBob.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return callReceived == 2 && !callIdBob.empty(); })); JAMI_INFO("Stop call between alice and Bob"); callStopped = 0; -- GitLab