diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index ad3f1dc866bebff383de4dc9dc03dc524f59d990..99e08a4175ec071377a314ee5f5db34e6b216636 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -1519,6 +1519,8 @@ JamiAccount::doRegister() } else { doRegister_(); } + + cacheTurnServers(); // reset cache for TURN servers } std::vector<std::string> @@ -1987,7 +1989,9 @@ JamiAccount::connectivityChanged() } dht_->connectivityChanged(); - setPublishedAddress({}); // reset cache + // reset cache + setPublishedAddress({}); + cacheTurnServers(); } bool @@ -2642,4 +2646,69 @@ JamiAccount::setActiveCodecs(const std::vector<unsigned>& list) } } +void +JamiAccount::cacheTurnServers() +{ + // The resolution of the TURN server can take quite some time (if timeout). + // So, run this in its own io thread to avoid to block the main thread. + dht::ThreadPool::io().run([this] { + // Avoid multiple refresh + if (isRefreshing_.exchange(true)) return; + if (!turnEnabled_) { + // In this case, we do not use any TURN server + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + cacheTurnV4_.reset(); + cacheTurnV6_.reset(); + isRefreshing_ = false; + return; + } + JAMI_INFO("Refresh cache for TURN server resolution"); + // Retrieve old cached value if available. + // This means that we directly get the correct value when launching the application on the same network + std::string server = turnServer_.empty() ? DEFAULT_TURN_SERVER : turnServer_; + fileutils::recursive_mkdir(cachePath_ + DIR_SEPARATOR_STR + "domains", 0700); + auto pathV4 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v4." + server; + if (auto turnV4File = std::ifstream(pathV4)) { + std::string content((std::istreambuf_iterator<char>(turnV4File)), std::istreambuf_iterator<char>()); + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + cacheTurnV4_ = std::make_unique<IpAddr>(content, AF_INET); + } + auto pathV6 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v6." + server; + if (auto turnV6File = std::ifstream(pathV6)) { + std::string content((std::istreambuf_iterator<char>(turnV6File)), std::istreambuf_iterator<char>()); + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + cacheTurnV6_ = std::make_unique<IpAddr>(content, AF_INET6); + } + // Resolve just in case. The user can have a different connectivity + auto turnV4 = IpAddr {server, AF_INET}; + { + if (turnV4) { + // Cache value to avoid a delay when starting up Jami + std::ofstream turnV4File(pathV4); + turnV4File << turnV4.toString(); + } else { + fileutils::remove(pathV4, true); + } + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + // Update TURN + cacheTurnV4_ = std::make_unique<IpAddr>(std::move(turnV4)); + } + auto turnV6 = IpAddr {server.empty() ? DEFAULT_TURN_SERVER : server, AF_INET6}; + { + if (turnV6) { + // Cache value to avoid a delay when starting up Jami + std::ofstream turnV6File(pathV6); + turnV6File << turnV6.toString(); + } else { + fileutils::remove(pathV6, true); + } + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + // Update TURN + cacheTurnV6_ = std::make_unique<IpAddr>(std::move(turnV6)); + } + isRefreshing_ = false; + JAMI_INFO("Cache refreshed for TURN resolution"); + }); +} + } // namespace jami diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 8b053481f8763df9319ee1f20502436e8b4ce10a..0596524400399dd4dc6c8448f3dc17d596c06f4e 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -646,6 +646,16 @@ private: bool accountPublish_ {false}; std::shared_ptr<RepeatedTask> eventHandler {}; + + /** + * Avoid to refresh the cache multiple times + */ + std::atomic_bool isRefreshing_ {false}; + /** + * This will cache the turn server resolution each time we launch + * Jami, or for each connectivityChange() + */ + void cacheTurnServers(); }; static inline std::ostream& operator<< (std::ostream& os, const JamiAccount& acc) diff --git a/src/jamidht/p2p.cpp b/src/jamidht/p2p.cpp index 89a6a65271616bb919f6ce02d4e5477946624d0a..2a0cf2d8a5a57ea1289cebb6f4af29ba31a9435c 100644 --- a/src/jamidht/p2p.cpp +++ b/src/jamidht/p2p.cpp @@ -548,8 +548,14 @@ DhtPeerConnector::Impl::turnConnect() auto username = details[Conf::CONFIG_TURN_SERVER_UNAME]; auto password = details[Conf::CONFIG_TURN_SERVER_PWD]; + auto turnCache = account.turnCache(); + auto turn_param_v4 = TurnTransportParams {}; - turn_param_v4.server = IpAddr {server.empty() ? "turn.jami.net" : server}; + if (turnCache[0]) { + turn_param_v4.server = *turnCache[0]; + } else { + turn_param_v4.server = IpAddr {server.empty() ? "turn.jami.net" : server}; + } turn_param_v4.realm = realm.empty() ? "ring" : realm; turn_param_v4.username = username.empty() ? "ring" : username; turn_param_v4.password = password.empty() ? "ring" : password; @@ -576,6 +582,11 @@ DhtPeerConnector::Impl::turnConnect() if (!turnAuthv6_ || !turnAuthv6_->isReady()) { auto turn_param_v6 = turn_param_v4; + if (turnCache[1]) { + turn_param_v6.server = *turnCache[1]; + } else { + turn_param_v6.server = IpAddr {server.empty() ? "turn.jami.net" : server}; + } turn_param_v6.authorized_family = PJ_AF_INET6; turnAuthv6_ = std::make_unique<TurnTransport>(turn_param_v6); } diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp index 0997c765ec2f9c254abe06c847f753b69907c7d5..9427b136345da2b092939205282efd3e6fe82680 100644 --- a/src/sip/sipaccountbase.cpp +++ b/src/sip/sipaccountbase.cpp @@ -370,15 +370,35 @@ SIPAccountBase::getIceOptions() const noexcept { auto opts = Account::getIceOptions(); // Local copy of global account ICE settings - // Note: we don't check of servers pre-existance, let underlaying stack do the job if (stunEnabled_) opts.stunServers.emplace_back(StunServerInfo().setUri(stunServer_)); - if (turnEnabled_) - opts.turnServers.emplace_back(TurnServerInfo() - .setUri(turnServer_) - .setUsername(turnServerUserName_) - .setPassword(turnServerPwd_) - .setRealm(turnServerRealm_)); + if (turnEnabled_) { + auto cached = false; + std::lock_guard<std::mutex> lk(cachedTurnMutex_); + cached = cacheTurnV4_ || cacheTurnV6_; + if (cacheTurnV4_ && *cacheTurnV4_) { + opts.turnServers.emplace_back(TurnServerInfo() + .setUri(*cacheTurnV4_) + .setUsername(turnServerUserName_) + .setPassword(turnServerPwd_) + .setRealm(turnServerRealm_)); + } + if (cacheTurnV6_ && *cacheTurnV6_) { + opts.turnServers.emplace_back(TurnServerInfo() + .setUri(*cacheTurnV4_) + .setUsername(turnServerUserName_) + .setPassword(turnServerPwd_) + .setRealm(turnServerRealm_)); + } + // Nothing cached, so do the resolution + if (!cached) { + opts.turnServers.emplace_back(TurnServerInfo() + .setUri(turnServer_) + .setUsername(turnServerUserName_) + .setPassword(turnServerPwd_) + .setRealm(turnServerRealm_)); + } + } return opts; } diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h index 6235313e9db6449662f3f41c710dea7bed4550aa..ea9ab75f72bd292f8708ed6ac43cf1cebfd1a8a9 100644 --- a/src/sip/sipaccountbase.h +++ b/src/sip/sipaccountbase.h @@ -298,6 +298,20 @@ public: public: // overloaded methods virtual void flush() override; + /** + * Return current turn resolved addresses + * @return {unique_ptr(v4 resolved), unique_ptr(v6 resolved)} + */ + std::array<std::unique_ptr<IpAddr>, 2> turnCache() { + std::lock_guard<std::mutex> lk {cachedTurnMutex_}; + std::array<std::unique_ptr<IpAddr>, 2> result = {}; + if (cacheTurnV4_ && *cacheTurnV4_) + result[0] = std::make_unique<IpAddr>(*cacheTurnV4_); + if (cacheTurnV6_ && *cacheTurnV6_) + result[1] = std::make_unique<IpAddr>(*cacheTurnV6_); + return result; + } + protected: virtual void serialize(YAML::Emitter &out) const override; virtual void serializeTls(YAML::Emitter &out) const; @@ -445,6 +459,10 @@ protected: static constexpr size_t MAX_WAITING_MESSAGES_SIZE = 1000; std::deque<DRing::Message> lastMessages_; + mutable std::mutex cachedTurnMutex_ {}; + std::unique_ptr<IpAddr> cacheTurnV4_ {}; + std::unique_ptr<IpAddr> cacheTurnV6_ {}; + private: NON_COPYABLE(SIPAccountBase);