blob: 652a10565c0aaec044716f8db4eb8075c6ec61f5 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
Adrien Béraudcb753622023-07-17 22:32:49 -04002 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
Adrien Béraud612b55b2023-05-29 10:42:04 -04003 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (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
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * 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#include "connectionmanager.h"
18#include "peer_connection.h"
19#include "upnp/upnp_control.h"
20#include "certstore.h"
21#include "fileutils.h"
22#include "sip_utils.h"
23#include "string_utils.h"
24
25#include <opendht/crypto.h>
26#include <opendht/thread_pool.h>
27#include <opendht/value.h>
28#include <asio.hpp>
29
30#include <algorithm>
31#include <mutex>
32#include <map>
33#include <condition_variable>
34#include <set>
35#include <charconv>
Morteza Namvar5f639522023-07-04 17:08:58 -040036#include <fstream>
Adrien Béraud612b55b2023-05-29 10:42:04 -040037
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040038namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040039static constexpr std::chrono::seconds DHT_MSG_TIMEOUT {30};
40static constexpr uint64_t ID_MAX_VAL = 9007199254740992;
41
42using ValueIdDist = std::uniform_int_distribution<dht::Value::Id>;
Adrien Béraud75754b22023-10-17 09:16:06 -040043
Amna31791e52023-08-03 12:40:57 -040044std::string
45callbackIdToString(const dhtnet::DeviceId& did, const dht::Value::Id& vid)
46{
47 return fmt::format("{} {}", did.to_view(), vid);
48}
Adrien Béraud612b55b2023-05-29 10:42:04 -040049
Adrien Béraud75754b22023-10-17 09:16:06 -040050std::pair<dhtnet::DeviceId, dht::Value::Id> parseCallbackId(std::string_view ci)
Amna31791e52023-08-03 12:40:57 -040051{
52 auto sep = ci.find(' ');
53 std::string_view deviceIdString = ci.substr(0, sep);
54 std::string_view vidString = ci.substr(sep + 1);
55
56 dhtnet::DeviceId deviceId(deviceIdString);
Andreas Traczyk0da84a72024-04-30 20:40:14 -040057 dht::Value::Id vid = std::stoull(std::string(vidString), nullptr, 10);
Adrien Béraud75754b22023-10-17 09:16:06 -040058 return {deviceId, vid};
Amna31791e52023-08-03 12:40:57 -040059}
Amna81221ad2023-09-14 17:33:26 -040060
61std::shared_ptr<ConnectionManager::Config>
62createConfig(std::shared_ptr<ConnectionManager::Config> config_)
63{
64 if (!config_->certStore){
65 config_->certStore = std::make_shared<dhtnet::tls::CertificateStore>("client", config_->logger);
66 }
67 if (!config_->dht) {
68 dht::DhtRunner::Config dhtConfig;
69 dhtConfig.dht_config.id = config_->id;
70 dhtConfig.threaded = true;
71 dht::DhtRunner::Context dhtContext;
72 dhtContext.certificateStore = [c = config_->certStore](const dht::InfoHash& pk_id) {
73 std::vector<std::shared_ptr<dht::crypto::Certificate>> ret;
74 if (auto cert = c->getCertificate(pk_id.toString()))
75 ret.emplace_back(std::move(cert));
76 return ret;
77 };
78 config_->dht = std::make_shared<dht::DhtRunner>();
79 config_->dht->run(dhtConfig, std::move(dhtContext));
80 config_->dht->bootstrap("bootstrap.jami.net");
81 }
82 if (!config_->factory){
83 config_->factory = std::make_shared<IceTransportFactory>(config_->logger);
84 }
85 return config_;
86}
87
Adrien Béraud612b55b2023-05-29 10:42:04 -040088struct ConnectionInfo
89{
90 ~ConnectionInfo()
91 {
92 if (socket_)
93 socket_->join();
94 }
95
96 std::mutex mutex_ {};
97 bool responseReceived_ {false};
98 PeerConnectionRequest response_ {};
99 std::unique_ptr<IceTransport> ice_ {nullptr};
100 // Used to store currently non ready TLS Socket
101 std::unique_ptr<TlsSocketEndpoint> tls_ {nullptr};
102 std::shared_ptr<MultiplexedSocket> socket_ {};
Adrien Béraud75754b22023-10-17 09:16:06 -0400103 std::set<dht::Value::Id> cbIds_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400104
105 std::function<void(bool)> onConnected_;
106 std::unique_ptr<asio::steady_timer> waitForAnswer_ {};
Adrien Béraud75754b22023-10-17 09:16:06 -0400107
108 void shutdown() {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500109 std::lock_guard lk(mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400110 if (tls_)
111 tls_->shutdown();
112 if (socket_)
113 socket_->shutdown();
114 if (waitForAnswer_)
115 waitForAnswer_->cancel();
116 if (ice_) {
117 dht::ThreadPool::io().run(
118 [ice = std::shared_ptr<IceTransport>(std::move(ice_))] {});
119 }
120 }
121
122 std::map<std::string, std::string>
123 getInfo(const DeviceId& deviceId, dht::Value::Id valueId, tls::CertificateStore& certStore) const
124 {
125 std::map<std::string, std::string> connectionInfo;
126 connectionInfo["id"] = callbackIdToString(deviceId, valueId);
127 connectionInfo["device"] = deviceId.toString();
128 auto cert = tls_ ? tls_->peerCertificate() : (socket_ ? socket_->peerCertificate() : nullptr);
129 if (not cert)
130 cert = certStore.getCertificate(deviceId.toString());
131 if (cert) {
132 connectionInfo["peer"] = cert->issuer->getId().toString();
133 }
134 if (socket_) {
135 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::Connected));
136 connectionInfo["remoteAddress"] = socket_->getRemoteAddress();
137 } else if (tls_) {
138 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::TLS));
139 connectionInfo["remoteAddress"] = tls_->getRemoteAddress();
140 } else if(ice_) {
141 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::ICE));
142 connectionInfo["remoteAddress"] = ice_->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
143 }
144 return connectionInfo;
145 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400146};
147
Adrien Béraud75754b22023-10-17 09:16:06 -0400148struct PendingCb {
149 std::string name;
150 ConnectCallback cb;
Adrien Béraudb941e922023-10-16 12:56:14 -0400151 bool requested {false};
Adrien Béraud75754b22023-10-17 09:16:06 -0400152};
153
154struct DeviceInfo {
155 const DeviceId deviceId;
156 mutable std::mutex mtx_ {};
157 std::map<dht::Value::Id, std::shared_ptr<ConnectionInfo>> info;
158 std::map<dht::Value::Id, PendingCb> connecting;
159 std::map<dht::Value::Id, PendingCb> waiting;
160 DeviceInfo(DeviceId id) : deviceId {id} {}
161
162 inline bool isConnecting() const {
163 return !connecting.empty() || !waiting.empty();
164 }
165
166 inline bool empty() const {
167 return info.empty() && connecting.empty() && waiting.empty();
168 }
169
170 dht::Value::Id newId(std::mt19937_64& rand) const {
171 ValueIdDist dist(1, ID_MAX_VAL);
172 dht::Value::Id id;
173 do {
174 id = dist(rand);
175 } while (info.find(id) != info.end()
176 || connecting.find(id) != connecting.end()
177 || waiting.find(id) != waiting.end());
178 return id;
179 }
180
181 std::shared_ptr<ConnectionInfo> getConnectedInfo() const {
182 for (auto& [id, ci] : info) {
183 if (ci->socket_)
184 return ci;
185 }
186 return {};
187 }
188
189 std::vector<PendingCb> extractPendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true)
190 {
191 std::vector<PendingCb> ret;
192 if (vid == 0) {
193 // Extract all pending callbacks
194 ret.reserve(connecting.size() + waiting.size());
195 for (auto& [vid, cb] : connecting)
196 ret.emplace_back(std::move(cb));
197 connecting.clear();
198 for (auto& [vid, cb] : waiting)
199 ret.emplace_back(std::move(cb));
200 waiting.clear();
201 } else if (auto n = waiting.extract(vid)) {
202 // If it's a waiting operation, just move it
203 ret.emplace_back(std::move(n.mapped()));
204 } else if (auto n = connecting.extract(vid)) {
205 ret.emplace_back(std::move(n.mapped()));
206 // If sock is nullptr, execute if it's the last connecting operation
207 // If accepted is false, it means that underlying socket is ok, but channel is declined
208 if (!sock && connecting.empty() && accepted) {
209 for (auto& [vid, cb] : waiting)
210 ret.emplace_back(std::move(cb));
211 waiting.clear();
212 for (auto& [vid, cb] : connecting)
213 ret.emplace_back(std::move(cb));
214 connecting.clear();
215 }
216 }
217 return ret;
218 }
219
220 std::vector<std::shared_ptr<ConnectionInfo>> extractUnusedConnections() {
221 std::vector<std::shared_ptr<ConnectionInfo>> unused {};
222 for (auto& [id, info] : info)
223 unused.emplace_back(std::move(info));
224 info.clear();
225 return unused;
226 }
227
228 void executePendingOperations(std::unique_lock<std::mutex>& lock, dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
229 auto ops = extractPendingOperations(vid, sock, accepted);
230 lock.unlock();
231 for (auto& cb : ops)
232 cb.cb(sock, deviceId);
233 }
234 void executePendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500235 std::unique_lock lock(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400236 executePendingOperations(lock, vid, sock, accepted);
237 }
238
Adrien Béraudb941e922023-10-16 12:56:14 -0400239 bool isConnecting(const std::string& name) const {
Adrien Béraud75754b22023-10-17 09:16:06 -0400240 for (const auto& [id, pc]: connecting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400241 if (pc.name == name)
242 return true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400243 for (const auto& [id, pc]: waiting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400244 if (pc.name == name)
245 return true;
246 return false;
247 }
248 std::map<dht::Value::Id, std::string> requestPendingOps() {
249 std::map<dht::Value::Id, std::string> ret;
250 for (auto& [id, pc]: connecting) {
251 if (!pc.requested) {
252 ret[id] = pc.name;
253 pc.requested = true;
254 }
255 }
256 for (auto& [id, pc]: waiting) {
257 if (!pc.requested) {
258 ret[id] = pc.name;
259 pc.requested = true;
260 }
261 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400262 return ret;
263 }
264
265 std::vector<std::map<std::string, std::string>>
266 getConnectionList(tls::CertificateStore& certStore) const {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500267 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400268 std::vector<std::map<std::string, std::string>> ret;
Adrien Béraudd5ec7a82023-10-28 18:07:03 -0400269 ret.reserve(info.size() + connecting.size() + waiting.size());
Adrien Béraud75754b22023-10-17 09:16:06 -0400270 for (auto& [id, ci] : info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500271 std::lock_guard lk(ci->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400272 ret.emplace_back(ci->getInfo(deviceId, id, certStore));
273 }
274 auto cert = certStore.getCertificate(deviceId.toString());
275 for (const auto& [vid, ci] : connecting) {
276 ret.emplace_back(std::map<std::string, std::string> {
277 {"id", callbackIdToString(deviceId, vid)},
278 {"status", std::to_string(static_cast<int>(ConnectionStatus::Connecting))},
279 {"device", deviceId.toString()},
280 {"peer", cert ? cert->issuer->getId().toString() : ""}
281 });
282 }
283 for (const auto& [vid, ci] : waiting) {
284 ret.emplace_back(std::map<std::string, std::string> {
285 {"id", callbackIdToString(deviceId, vid)},
286 {"status", std::to_string(static_cast<int>(ConnectionStatus::Waiting))},
287 {"device", deviceId.toString()},
288 {"peer", cert ? cert->issuer->getId().toString() : ""}
289 });
290 }
291 return ret;
292 }
293};
294
295class DeviceInfoSet {
296public:
297 std::shared_ptr<DeviceInfo> getDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500298 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400299 auto it = infos_.find(deviceId);
300 if (it != infos_.end())
301 return it->second;
302 return {};
303 }
304
305 std::vector<std::shared_ptr<DeviceInfo>> getDeviceInfos() {
306 std::vector<std::shared_ptr<DeviceInfo>> deviceInfos;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500307 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400308 deviceInfos.reserve(infos_.size());
309 for (auto& [deviceId, info] : infos_)
310 deviceInfos.emplace_back(info);
311 return deviceInfos;
312 }
313
314 std::shared_ptr<DeviceInfo> createDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500315 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400316 auto& info = infos_[deviceId];
317 if (!info)
318 info = std::make_shared<DeviceInfo>(deviceId);
319 return info;
320 }
321
322 bool removeDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500323 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400324 return infos_.erase(deviceId) != 0;
325 }
326
327 std::shared_ptr<ConnectionInfo> getInfo(const DeviceId& deviceId, const dht::Value::Id& id) {
328 if (auto info = getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500329 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400330 auto it = info->info.find(id);
331 if (it != info->info.end())
332 return it->second;
333 }
334 return {};
335 }
336
337 std::vector<std::shared_ptr<ConnectionInfo>> getConnectedInfos() {
338 auto deviceInfos = getDeviceInfos();
339 std::vector<std::shared_ptr<ConnectionInfo>> ret;
340 ret.reserve(deviceInfos.size());
341 for (auto& info : deviceInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500342 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400343 for (auto& [id, ci] : info->info) {
344 if (ci->socket_)
345 ret.emplace_back(ci);
346 }
347 }
348 return ret;
349 }
350 std::vector<std::shared_ptr<DeviceInfo>> shutdown() {
351 std::vector<std::shared_ptr<DeviceInfo>> ret;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500352 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400353 ret.reserve(infos_.size());
354 for (auto& [deviceId, info] : infos_) {
355 ret.emplace_back(std::move(info));
356 }
357 infos_.clear();
358 return ret;
359 }
360
361private:
362 std::mutex mtx_ {};
363 std::map<DeviceId, std::shared_ptr<DeviceInfo>> infos_ {};
364};
365
Adrien Béraud612b55b2023-05-29 10:42:04 -0400366class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl>
367{
368public:
369 explicit Impl(std::shared_ptr<ConnectionManager::Config> config_)
Amna81221ad2023-09-14 17:33:26 -0400370 : config_ {std::move(createConfig(config_))}
Adrien Béraudd8b6a402023-12-08 14:19:25 -0500371 , rand_ {config_->rng ? *config_->rng : dht::crypto::getSeededRandomEngine<std::mt19937_64>()}
Adrien Béraud5aec4102024-02-22 14:15:56 -0500372 , treatedMessages_ {config_->cachePath / "treatedMessages"}
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400373 {
Amna81221ad2023-09-14 17:33:26 -0400374 if(!config_->ioContext) {
375 config_->ioContext = std::make_shared<asio::io_context>();
376 ioContextRunner_ = std::make_unique<std::thread>([context = config_->ioContext, l=config_->logger]() {
377 try {
378 auto work = asio::make_work_guard(*context);
379 context->run();
380 } catch (const std::exception& ex) {
381 if (l) l->error("Exception: {}", ex.what());
382 }
383 });
384 }
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400385 }
Amna81221ad2023-09-14 17:33:26 -0400386 ~Impl() {
387 if (ioContextRunner_) {
388 if (config_->logger) config_->logger->debug("ConnectionManager: stopping io_context thread");
389 config_->ioContext->stop();
390 ioContextRunner_->join();
391 ioContextRunner_.reset();
392 }
393 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400394
395 std::shared_ptr<dht::DhtRunner> dht() { return config_->dht; }
396 const dht::crypto::Identity& identity() const { return config_->id; }
397
Adrien Béraud75754b22023-10-17 09:16:06 -0400398 void shutdown()
Adrien Béraud612b55b2023-05-29 10:42:04 -0400399 {
Adrien Béraud75754b22023-10-17 09:16:06 -0400400 if (isDestroying_.exchange(true))
401 return;
402 std::vector<std::shared_ptr<ConnectionInfo>> unused;
403 std::vector<std::pair<DeviceId, std::vector<PendingCb>>> pending;
404 for (auto& dinfo: infos_.shutdown()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500405 std::lock_guard lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400406 auto p = dinfo->extractPendingOperations(0, nullptr, false);
407 if (!p.empty())
408 pending.emplace_back(dinfo->deviceId, std::move(p));
409 auto uc = dinfo->extractUnusedConnections();
410 unused.insert(unused.end(), std::make_move_iterator(uc.begin()), std::make_move_iterator(uc.end()));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400411 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400412 for (auto& info: unused)
413 info->shutdown();
414 for (auto& op: pending)
415 for (auto& cb: op.second)
416 cb.cb(nullptr, op.first);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400417 if (!unused.empty())
Amna81221ad2023-09-14 17:33:26 -0400418 dht::ThreadPool::io().run([infos = std::move(unused)]() mutable {
419 infos.clear();
420 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400421 }
422
Adrien Béraud75754b22023-10-17 09:16:06 -0400423 void connectDeviceStartIce(const std::shared_ptr<ConnectionInfo>& info,
424 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400425 const dht::Value::Id& vid,
426 const std::string& connType,
427 std::function<void(bool)> onConnected);
Adrien Béraud75754b22023-10-17 09:16:06 -0400428 void onResponse(const asio::error_code& ec, const std::weak_ptr<ConnectionInfo>& info, const DeviceId& deviceId, const dht::Value::Id& vid);
429 bool connectDeviceOnNegoDone(const std::weak_ptr<DeviceInfo>& dinfo,
430 const std::shared_ptr<ConnectionInfo>& info,
431 const DeviceId& deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400432 const std::string& name,
433 const dht::Value::Id& vid,
434 const std::shared_ptr<dht::crypto::Certificate>& cert);
435 void connectDevice(const DeviceId& deviceId,
436 const std::string& uri,
437 ConnectCallback cb,
438 bool noNewSocket = false,
439 bool forceNewSocket = false,
440 const std::string& connType = "");
Amna0cf544d2023-07-25 14:25:09 -0400441 void connectDevice(const dht::InfoHash& deviceId,
442 const std::string& uri,
443 ConnectCallbackLegacy cb,
444 bool noNewSocket = false,
445 bool forceNewSocket = false,
446 const std::string& connType = "");
447
Adrien Béraud612b55b2023-05-29 10:42:04 -0400448 void connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
449 const std::string& name,
450 ConnectCallback cb,
451 bool noNewSocket = false,
452 bool forceNewSocket = false,
453 const std::string& connType = "");
454 /**
455 * Send a ChannelRequest on the TLS socket. Triggers cb when ready
456 * @param sock socket used to send the request
457 * @param name channel's name
458 * @param vid channel's id
459 * @param deviceId to identify the linked ConnectCallback
460 */
Adrien Béraud75754b22023-10-17 09:16:06 -0400461 void sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfo,
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400462 const std::weak_ptr<ConnectionInfo>& cinfo,
Adrien Béraud75754b22023-10-17 09:16:06 -0400463 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400464 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400465 const dht::Value::Id& vid);
466 /**
467 * Triggered when a PeerConnectionRequest comes from the DHT
468 */
469 void answerTo(IceTransport& ice,
470 const dht::Value::Id& id,
471 const std::shared_ptr<dht::crypto::PublicKey>& fromPk);
Adrien Béraud75754b22023-10-17 09:16:06 -0400472 bool onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req);
473 bool onRequestOnNegoDone(const std::weak_ptr<DeviceInfo>& dinfo, const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400474 void onDhtPeerRequest(const PeerConnectionRequest& req,
475 const std::shared_ptr<dht::crypto::Certificate>& cert);
Adrien Béraud75754b22023-10-17 09:16:06 -0400476 /**
477 * Triggered when a new TLS socket is ready to use
478 * @param ok If succeed
479 * @param deviceId Related device
480 * @param vid vid of the connection request
481 * @param name non empty if TLS was created by connectDevice()
482 */
483 void onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
484 const std::shared_ptr<ConnectionInfo>& info,
485 bool ok,
486 const DeviceId& deviceId,
487 const dht::Value::Id& vid,
488 const std::string& name = "");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400489
Adrien Béraud75754b22023-10-17 09:16:06 -0400490 void addNewMultiplexedSocket(const std::weak_ptr<DeviceInfo>& dinfo, const DeviceId& deviceId, const dht::Value::Id& vid, const std::shared_ptr<ConnectionInfo>& info);
Adrien Béraud1addf952023-09-30 17:38:35 -0400491 void onPeerResponse(PeerConnectionRequest&& req);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400492 void onDhtConnected(const dht::crypto::PublicKey& devicePk);
493
Adrien Béraud75754b22023-10-17 09:16:06 -0400494
Adrien Béraud612b55b2023-05-29 10:42:04 -0400495 const std::shared_future<tls::DhParams> dhParams() const;
496 tls::CertificateStore& certStore() const { return *config_->certStore; }
497
498 mutable std::mutex messageMutex_ {};
Adrien Béraud5aec4102024-02-22 14:15:56 -0500499 fileutils::IdList treatedMessages_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400500
501 /// \return true if the given DHT message identifier has been treated
502 /// \note if message has not been treated yet this method st/ore this id and returns true at
503 /// further calls
Adrien Béraud5aec4102024-02-22 14:15:56 -0500504 bool isMessageTreated(dht::Value::Id id);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400505
506 const std::shared_ptr<dht::log::Logger>& logger() const { return config_->logger; }
507
508 /**
509 * Published IPv4/IPv6 addresses, used only if defined by the user in account
510 * configuration
511 *
512 */
513 IpAddr publishedIp_[2] {};
514
Adrien Béraud612b55b2023-05-29 10:42:04 -0400515 /**
516 * interface name on which this account is bound
517 */
518 std::string interface_ {"default"};
519
520 /**
521 * Get the local interface name on which this account is bound.
522 */
523 const std::string& getLocalInterface() const { return interface_; }
524
525 /**
526 * Get the published IP address, fallbacks to NAT if family is unspecified
527 * Prefers the usage of IPv4 if possible.
528 */
529 IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
530
531 /**
532 * Set published IP address according to given family
533 */
534 void setPublishedAddress(const IpAddr& ip_addr);
535
536 /**
537 * Store the local/public addresses used to register
538 */
539 void storeActiveIpAddress(std::function<void()>&& cb = {});
540
541 /**
542 * Create and return ICE options.
543 */
544 void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
545 IceTransportOptions getIceOptions() const noexcept;
546
547 /**
548 * Inform that a potential peer device have been found.
549 * Returns true only if the device certificate is a valid device certificate.
550 * In that case (true is returned) the account_id parameter is set to the peer account ID.
551 */
552 static bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
553 dht::InfoHash& account_id, const std::shared_ptr<Logger>& logger);
554
555 bool findCertificate(const dht::PkId& id,
556 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
Sébastien Blin34086512023-07-25 09:52:14 -0400557 bool findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400558
Adrien Béraud612b55b2023-05-29 10:42:04 -0400559 std::shared_ptr<ConnectionManager::Config> config_;
Amna81221ad2023-09-14 17:33:26 -0400560 std::unique_ptr<std::thread> ioContextRunner_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400561
Adrien Béraud75754b22023-10-17 09:16:06 -0400562 mutable std::mutex randMtx_;
563 mutable std::mt19937_64 rand_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400564
565 iOSConnectedCallback iOSConnectedCb_ {};
566
Adrien Béraud75754b22023-10-17 09:16:06 -0400567 DeviceInfoSet infos_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400568
569 ChannelRequestCallback channelReqCb_ {};
570 ConnectionReadyCallback connReadyCb_ {};
571 onICERequestCallback iceReqCb_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400572 std::atomic_bool isDestroying_ {false};
573};
574
575void
576ConnectionManager::Impl::connectDeviceStartIce(
Adrien Béraud75754b22023-10-17 09:16:06 -0400577 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400578 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
579 const dht::Value::Id& vid,
580 const std::string& connType,
581 std::function<void(bool)> onConnected)
582{
583 auto deviceId = devicePk->getLongId();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400584 if (!info) {
585 onConnected(false);
586 return;
587 }
588
Adrien Béraud024c46f2024-03-02 23:53:18 -0500589 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400590 auto& ice = info->ice_;
591
592 if (!ice) {
593 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400594 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400595 onConnected(false);
596 return;
597 }
598
599 auto iceAttributes = ice->getLocalAttributes();
600 std::ostringstream icemsg;
601 icemsg << iceAttributes.ufrag << "\n";
602 icemsg << iceAttributes.pwd << "\n";
603 for (const auto& addr : ice->getLocalCandidates(1)) {
604 icemsg << addr << "\n";
605 if (config_->logger)
Sébastien Blinaec46fc2023-07-25 15:43:10 -0400606 config_->logger->debug("[device {}] Added local ICE candidate {}", deviceId, addr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400607 }
608
609 // Prepare connection request as a DHT message
610 PeerConnectionRequest val;
611
612 val.id = vid; /* Random id for the message unicity */
613 val.ice_msg = icemsg.str();
614 val.connType = connType;
615
616 auto value = std::make_shared<dht::Value>(std::move(val));
617 value->user_type = "peer_request";
618
619 // Send connection request through DHT
620 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400621 config_->logger->debug("[device {}] Sending connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400622 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
623 + devicePk->getId().toString()),
624 devicePk,
625 value,
626 [l=config_->logger,deviceId](bool ok) {
627 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -0400628 l->debug("[device {}] Sent connection request. Put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -0400629 deviceId,
630 (ok ? "ok" : "failed"));
631 });
632 // Wait for call to onResponse() operated by DHT
633 if (isDestroying_) {
634 onConnected(true); // This avoid to wait new negotiation when destroying
635 return;
636 }
637
638 info->onConnected_ = std::move(onConnected);
639 info->waitForAnswer_ = std::make_unique<asio::steady_timer>(*config_->ioContext,
640 std::chrono::steady_clock::now()
641 + DHT_MSG_TIMEOUT);
642 info->waitForAnswer_->async_wait(
Adrien Béraud75754b22023-10-17 09:16:06 -0400643 std::bind(&ConnectionManager::Impl::onResponse, this, std::placeholders::_1, info, deviceId, vid));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400644}
645
646void
647ConnectionManager::Impl::onResponse(const asio::error_code& ec,
Adrien Béraud75754b22023-10-17 09:16:06 -0400648 const std::weak_ptr<ConnectionInfo>& winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400649 const DeviceId& deviceId,
650 const dht::Value::Id& vid)
651{
652 if (ec == asio::error::operation_aborted)
653 return;
Adrien Béraud75754b22023-10-17 09:16:06 -0400654 auto info = winfo.lock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400655 if (!info)
656 return;
657
Adrien Béraud024c46f2024-03-02 23:53:18 -0500658 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400659 auto& ice = info->ice_;
660 if (isDestroying_) {
661 info->onConnected_(true); // The destructor can wake a pending wait here.
662 return;
663 }
664 if (!info->responseReceived_) {
665 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400666 config_->logger->error("[device {}] no response from DHT to ICE request.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400667 info->onConnected_(false);
668 return;
669 }
670
671 if (!info->ice_) {
672 info->onConnected_(false);
673 return;
674 }
675
676 auto sdp = ice->parseIceCandidates(info->response_.ice_msg);
677
678 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
679 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400680 config_->logger->warn("[device {}] start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400681 info->onConnected_(false);
682 return;
683 }
684 info->onConnected_(true);
685}
686
687bool
688ConnectionManager::Impl::connectDeviceOnNegoDone(
Adrien Béraud75754b22023-10-17 09:16:06 -0400689 const std::weak_ptr<DeviceInfo>& dinfo,
690 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400691 const DeviceId& deviceId,
692 const std::string& name,
693 const dht::Value::Id& vid,
694 const std::shared_ptr<dht::crypto::Certificate>& cert)
695{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400696 if (!info)
697 return false;
698
Adrien Béraud024c46f2024-03-02 23:53:18 -0500699 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400700 if (info->waitForAnswer_) {
701 // Negotiation is done and connected, go to handshake
702 // and avoid any cancellation at this point.
703 info->waitForAnswer_->cancel();
704 }
705 auto& ice = info->ice_;
706 if (!ice || !ice->isRunning()) {
707 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400708 config_->logger->error("[device {}] No ICE detected or not running", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400709 return false;
710 }
711
712 // Build socket
713 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
714 std::move(ice)),
715 true);
716
717 // Negotiate a TLS session
718 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400719 config_->logger->debug("[device {}] Start TLS session - Initied by connectDevice(). Launched by channel: {} - vid: {}", deviceId, name, vid);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400720 info->tls_ = std::make_unique<TlsSocketEndpoint>(std::move(endpoint),
721 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400722 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400723 identity(),
724 dhParams(),
725 *cert);
726
727 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -0400728 [w = weak_from_this(), dinfo, winfo=std::weak_ptr(info), deviceId = std::move(deviceId), vid = std::move(vid), name = std::move(name)](
Adrien Béraud612b55b2023-05-29 10:42:04 -0400729 bool ok) {
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500730 if (auto shared = w.lock())
Andreas Traczykb23278d2023-12-11 16:14:00 -0500731 if (auto info = winfo.lock()) {
732 shared->onTlsNegotiationDone(dinfo.lock(), info, ok, deviceId, vid, name);
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500733 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
Andreas Traczykb23278d2023-12-11 16:14:00 -0500734 dht::ThreadPool::io().run([info = std::move(info)] {});
735 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400736 });
737 return true;
738}
739
740void
741ConnectionManager::Impl::connectDevice(const DeviceId& deviceId,
742 const std::string& name,
743 ConnectCallback cb,
744 bool noNewSocket,
745 bool forceNewSocket,
746 const std::string& connType)
747{
748 if (!dht()) {
749 cb(nullptr, deviceId);
750 return;
751 }
752 if (deviceId.toString() == identity().second->getLongId().toString()) {
753 cb(nullptr, deviceId);
754 return;
755 }
756 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400757 [w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400758 deviceId,
759 name,
760 cb = std::move(cb),
761 noNewSocket,
762 forceNewSocket,
763 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
764 if (!cert) {
765 if (auto shared = w.lock())
766 if (shared->config_->logger)
767 shared->config_->logger->error(
768 "No valid certificate found for device {}",
769 deviceId);
770 cb(nullptr, deviceId);
771 return;
772 }
773 if (auto shared = w.lock()) {
774 shared->connectDevice(cert,
775 name,
776 std::move(cb),
777 noNewSocket,
778 forceNewSocket,
779 connType);
780 } else
781 cb(nullptr, deviceId);
782 });
783}
784
785void
Amna0cf544d2023-07-25 14:25:09 -0400786ConnectionManager::Impl::connectDevice(const dht::InfoHash& deviceId,
787 const std::string& name,
788 ConnectCallbackLegacy cb,
789 bool noNewSocket,
790 bool forceNewSocket,
791 const std::string& connType)
792{
793 if (!dht()) {
794 cb(nullptr, deviceId);
795 return;
796 }
797 if (deviceId.toString() == identity().second->getLongId().toString()) {
798 cb(nullptr, deviceId);
799 return;
800 }
801 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400802 [w = weak_from_this(),
Amna0cf544d2023-07-25 14:25:09 -0400803 deviceId,
804 name,
805 cb = std::move(cb),
806 noNewSocket,
807 forceNewSocket,
808 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
809 if (!cert) {
810 if (auto shared = w.lock())
811 if (shared->config_->logger)
812 shared->config_->logger->error(
813 "No valid certificate found for device {}",
814 deviceId);
815 cb(nullptr, deviceId);
816 return;
817 }
818 if (auto shared = w.lock()) {
819 shared->connectDevice(cert,
820 name,
Adrien Béraudd78d1ac2023-08-25 10:43:33 -0400821 [cb, deviceId](const std::shared_ptr<ChannelSocket>& sock, const DeviceId& /*did*/){
Amna0cf544d2023-07-25 14:25:09 -0400822 cb(sock, deviceId);
823 },
824 noNewSocket,
825 forceNewSocket,
826 connType);
827 } else
828 cb(nullptr, deviceId);
829 });
830}
831
832void
Adrien Béraud612b55b2023-05-29 10:42:04 -0400833ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
834 const std::string& name,
835 ConnectCallback cb,
836 bool noNewSocket,
837 bool forceNewSocket,
838 const std::string& connType)
839{
840 // Avoid dht operation in a DHT callback to avoid deadlocks
Adrien Béraud75754b22023-10-17 09:16:06 -0400841 dht::ThreadPool::computation().run([w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400842 name = std::move(name),
843 cert = std::move(cert),
844 cb = std::move(cb),
845 noNewSocket,
846 forceNewSocket,
847 connType] {
848 auto devicePk = cert->getSharedPublicKey();
849 auto deviceId = devicePk->getLongId();
850 auto sthis = w.lock();
851 if (!sthis || sthis->isDestroying_) {
852 cb(nullptr, deviceId);
853 return;
854 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400855 auto di = sthis->infos_.createDeviceInfo(deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -0500856 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400857
Adrien Béraud26365c92023-09-23 23:42:43 -0400858 dht::Value::Id vid;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400859 {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500860 std::lock_guard lkr(sthis->randMtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400861 vid = di->newId(sthis->rand_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400862 }
863
Adrien Béraud75754b22023-10-17 09:16:06 -0400864 // Check if already connecting
865 auto isConnectingToDevice = di->isConnecting();
866 // Note: we can be in a state where first
867 // socket is negotiated and first channel is pending
868 // so return only after we checked the info
Adrien Béraudb941e922023-10-16 12:56:14 -0400869 auto& diw = (isConnectingToDevice && !forceNewSocket)
870 ? di->waiting[vid]
871 : di->connecting[vid];
872 diw = PendingCb {name, std::move(cb)};
873
Adrien Béraud612b55b2023-05-29 10:42:04 -0400874 // Check if already negotiated
Adrien Béraud75754b22023-10-17 09:16:06 -0400875 if (auto info = di->getConnectedInfo()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500876 std::unique_lock lkc(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400877 if (auto sock = info->socket_) {
878 info->cbIds_.emplace(vid);
Adrien Béraudb941e922023-10-16 12:56:14 -0400879 diw.requested = true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400880 lkc.unlock();
881 lk.unlock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400882 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400883 sthis->config_->logger->debug("[device {}] Peer already connected. Add a new channel", deviceId);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400884 sthis->sendChannelRequest(di, info, sock, name, vid);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400885 return;
886 }
887 }
888
889 if (isConnectingToDevice && !forceNewSocket) {
890 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400891 sthis->config_->logger->debug("[device {}] Already connecting, wait for ICE negotiation", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400892 return;
893 }
894 if (noNewSocket) {
895 // If no new socket is specified, we don't try to generate a new socket
Adrien Béraud75754b22023-10-17 09:16:06 -0400896 di->executePendingOperations(lk, vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400897 return;
898 }
899
900 // Note: used when the ice negotiation fails to erase
901 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -0400902 auto eraseInfo = [w, diw=std::weak_ptr(di), vid] {
903 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500904 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400905 di->info.erase(vid);
906 auto ops = di->extractPendingOperations(vid, nullptr);
907 if (di->empty()) {
908 if (auto shared = w.lock())
909 shared->infos_.removeDeviceInfo(di->deviceId);
910 }
911 lk.unlock();
912 for (const auto& op: ops)
913 op.cb(nullptr, di->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400914 }
915 };
916
917 // If no socket exists, we need to initiate an ICE connection.
918 sthis->getIceOptions([w,
919 deviceId = std::move(deviceId),
920 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400921 diw=std::weak_ptr(di),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400922 name = std::move(name),
923 cert = std::move(cert),
924 vid,
925 connType,
926 eraseInfo](auto&& ice_config) {
927 auto sthis = w.lock();
928 if (!sthis) {
929 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
930 return;
931 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400932 auto info = std::make_shared<ConnectionInfo>();
933 auto winfo = std::weak_ptr(info);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400934 ice_config.tcpEnable = true;
935 ice_config.onInitDone = [w,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400936 devicePk = std::move(devicePk),
937 name = std::move(name),
938 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400939 diw,
940 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400941 vid,
942 connType,
943 eraseInfo](bool ok) {
944 dht::ThreadPool::io().run([w = std::move(w),
945 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400946 vid,
947 winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400948 eraseInfo,
949 connType, ok] {
950 auto sthis = w.lock();
951 if (!ok && sthis && sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -0400952 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", devicePk->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400953 if (!sthis || !ok) {
954 eraseInfo();
955 return;
956 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400957 sthis->connectDeviceStartIce(winfo.lock(), devicePk, vid, connType, [=](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400958 if (!ok) {
959 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
960 }
961 });
962 });
963 };
964 ice_config.onNegoDone = [w,
965 deviceId,
966 name,
967 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400968 diw,
969 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400970 vid,
971 eraseInfo](bool ok) {
972 dht::ThreadPool::io().run([w = std::move(w),
973 deviceId = std::move(deviceId),
974 name = std::move(name),
975 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400976 diw = std::move(diw),
977 winfo = std::move(winfo),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400978 vid = std::move(vid),
979 eraseInfo = std::move(eraseInfo),
980 ok] {
981 auto sthis = w.lock();
982 if (!ok && sthis && sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400983 sthis->config_->logger->error("[device {}] ICE negotiation failed.", deviceId);
Adrien Béraud75754b22023-10-17 09:16:06 -0400984 if (!sthis || !ok || !sthis->connectDeviceOnNegoDone(diw, winfo.lock(), deviceId, name, vid, cert))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400985 eraseInfo();
986 });
987 };
988
Adrien Béraud75754b22023-10-17 09:16:06 -0400989 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500990 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400991 di->info[vid] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400992 }
Adrien Béraud024c46f2024-03-02 23:53:18 -0500993 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400994 ice_config.master = false;
995 ice_config.streamsCount = 1;
996 ice_config.compCountPerStream = 1;
Sébastien Blin34086512023-07-25 09:52:14 -0400997 info->ice_ = sthis->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400998 if (!info->ice_) {
999 if (sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001000 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001001 eraseInfo();
1002 return;
1003 }
1004 // We need to detect any shutdown if the ice session is destroyed before going to the
1005 // TLS session;
1006 info->ice_->setOnShutdown([eraseInfo]() {
1007 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1008 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001009 try {
1010 info->ice_->initIceInstance(ice_config);
1011 } catch (const std::exception& e) {
1012 if (sthis->config_->logger)
1013 sthis->config_->logger->error("{}", e.what());
1014 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1015 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001016 });
1017 });
1018}
1019
1020void
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001021ConnectionManager::Impl::sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfow,
1022 const std::weak_ptr<ConnectionInfo>& cinfow,
Adrien Béraud75754b22023-10-17 09:16:06 -04001023 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001024 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001025 const dht::Value::Id& vid)
1026{
1027 auto channelSock = sock->addChannel(name);
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001028 if (!channelSock) {
1029 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001030 config_->logger->error("sendChannelRequest failed - unable to create channel");
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001031 if (auto info = dinfow.lock())
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001032 info->executePendingOperations(vid, nullptr);
1033 return;
1034 }
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001035 channelSock->onShutdown([dinfow, name, vid] {
1036 if (auto info = dinfow.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001037 info->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001038 });
1039 channelSock->onReady(
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001040 [dinfow, cinfow, wSock = std::weak_ptr(channelSock), name, vid](bool accepted) {
1041 if (auto dinfo = dinfow.lock()) {
1042 dinfo->executePendingOperations(vid, accepted ? wSock.lock() : nullptr, accepted);
Sébastien Blinad161572024-01-31 14:14:51 -05001043 // Always lock top-down cinfo->mutex
1044 dht::ThreadPool::io().run([cinfow, vid]() {
1045 if (auto cinfo = cinfow.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001046 std::lock_guard lk(cinfo->mutex_);
Sébastien Blinad161572024-01-31 14:14:51 -05001047 cinfo->cbIds_.erase(vid);
1048 }
1049 });
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001050 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001051 });
1052
1053 ChannelRequest val;
1054 val.name = channelSock->name();
1055 val.state = ChannelRequestState::REQUEST;
1056 val.channel = channelSock->channel();
1057 msgpack::sbuffer buffer(256);
1058 msgpack::pack(buffer, val);
1059
1060 std::error_code ec;
1061 int res = sock->write(CONTROL_CHANNEL,
1062 reinterpret_cast<const uint8_t*>(buffer.data()),
1063 buffer.size(),
1064 ec);
1065 if (res < 0) {
1066 // TODO check if we should handle errors here
1067 if (config_->logger)
Adrien Béraud75754b22023-10-17 09:16:06 -04001068 config_->logger->error("sendChannelRequest failed - error: {}", ec.message());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001069 }
1070}
1071
1072void
Adrien Béraud1addf952023-09-30 17:38:35 -04001073ConnectionManager::Impl::onPeerResponse(PeerConnectionRequest&& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001074{
1075 auto device = req.owner->getLongId();
Adrien Béraud75754b22023-10-17 09:16:06 -04001076 if (auto info = infos_.getInfo(device, req.id)) {
Adrien Béraud23852462023-07-22 01:46:27 -04001077 if (config_->logger)
1078 config_->logger->debug("[device {}] New response received", device);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001079 std::lock_guard lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001080 info->responseReceived_ = true;
1081 info->response_ = std::move(req);
1082 info->waitForAnswer_->expires_at(std::chrono::steady_clock::now());
1083 info->waitForAnswer_->async_wait(std::bind(&ConnectionManager::Impl::onResponse,
1084 this,
1085 std::placeholders::_1,
Adrien Béraud75754b22023-10-17 09:16:06 -04001086 std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -04001087 device,
1088 req.id));
1089 } else {
1090 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001091 config_->logger->warn("[device {}] Response received, but unable to find request", device);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001092 }
1093}
1094
1095void
1096ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1097{
1098 if (!dht())
1099 return;
1100 dht()->listen<PeerConnectionRequest>(
1101 dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()),
Adrien Béraud75754b22023-10-17 09:16:06 -04001102 [w = weak_from_this()](PeerConnectionRequest&& req) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001103 auto shared = w.lock();
1104 if (!shared)
1105 return false;
Adrien Béraud5aec4102024-02-22 14:15:56 -05001106 if (shared->isMessageTreated(req.id)) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001107 // Message already treated. Just ignore
1108 return true;
1109 }
1110 if (req.isAnswer) {
1111 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001112 shared->config_->logger->debug("[device {}] Received request answer", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001113 } else {
1114 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001115 shared->config_->logger->debug("[device {}] Received request", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001116 }
1117 if (req.isAnswer) {
Adrien Béraud1addf952023-09-30 17:38:35 -04001118 shared->onPeerResponse(std::move(req));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001119 } else {
1120 // Async certificate checking
Sébastien Blin34086512023-07-25 09:52:14 -04001121 shared->findCertificate(
Adrien Béraud612b55b2023-05-29 10:42:04 -04001122 req.from,
1123 [w, req = std::move(req)](
1124 const std::shared_ptr<dht::crypto::Certificate>& cert) mutable {
1125 auto shared = w.lock();
1126 if (!shared)
1127 return;
1128 dht::InfoHash peer_h;
1129 if (foundPeerDevice(cert, peer_h, shared->config_->logger)) {
1130#if TARGET_OS_IOS
1131 if (shared->iOSConnectedCb_(req.connType, peer_h))
1132 return;
1133#endif
1134 shared->onDhtPeerRequest(req, cert);
1135 } else {
1136 if (shared->config_->logger)
1137 shared->config_->logger->warn(
Adrien Béraud23852462023-07-22 01:46:27 -04001138 "[device {}] Received request from untrusted peer",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001139 req.owner->getLongId());
1140 }
1141 });
1142 }
1143
1144 return true;
1145 },
1146 dht::Value::UserTypeFilter("peer_request"));
1147}
1148
1149void
Adrien Béraud75754b22023-10-17 09:16:06 -04001150ConnectionManager::Impl::onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
1151 const std::shared_ptr<ConnectionInfo>& info,
1152 bool ok,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001153 const DeviceId& deviceId,
1154 const dht::Value::Id& vid,
1155 const std::string& name)
1156{
1157 if (isDestroying_)
1158 return;
1159 // Note: only handle pendingCallbacks here for TLS initied by connectDevice()
1160 // Note: if not initied by connectDevice() the channel name will be empty (because no channel
1161 // asked yet)
1162 auto isDhtRequest = name.empty();
1163 if (!ok) {
1164 if (isDhtRequest) {
1165 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001166 config_->logger->error("[device {}] TLS connection failure - Initied by DHT request. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001167 deviceId,
1168 name,
1169 vid);
1170 if (connReadyCb_)
1171 connReadyCb_(deviceId, "", nullptr);
1172 } else {
1173 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001174 config_->logger->error("[device {}] TLS connection failure - Initied by connectDevice. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001175 deviceId,
1176 name,
1177 vid);
Adrien Béraud75754b22023-10-17 09:16:06 -04001178 dinfo->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001179 }
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001180
Adrien Béraud024c46f2024-03-02 23:53:18 -05001181 std::unique_lock lk(dinfo->mtx_);
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001182 dinfo->info.erase(vid);
1183
1184 if (dinfo->empty()) {
1185 infos_.removeDeviceInfo(dinfo->deviceId);
1186 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001187 } else {
1188 // The socket is ready, store it
1189 if (isDhtRequest) {
1190 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001191 config_->logger->debug("[device {}] Connection is ready - Initied by DHT request. Vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001192 deviceId,
1193 vid);
1194 } else {
1195 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001196 config_->logger->debug("[device {}] Connection is ready - Initied by connectDevice(). channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001197 deviceId,
1198 name,
1199 vid);
1200 }
1201
Adrien Béraud75754b22023-10-17 09:16:06 -04001202 // Note: do not remove pending there it's done in sendChannelRequest
Adrien Béraud024c46f2024-03-02 23:53:18 -05001203 std::unique_lock lk2 {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001204 auto pendingIds = dinfo->requestPendingOps();
Adrien Béraud75754b22023-10-17 09:16:06 -04001205 lk2.unlock();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001206 std::unique_lock lk {info->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001207 addNewMultiplexedSocket(dinfo, deviceId, vid, info);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001208 // Finally, open the channel and launch pending callbacks
Adrien Béraud75754b22023-10-17 09:16:06 -04001209 lk.unlock();
1210 for (const auto& [id, name]: pendingIds) {
1211 if (config_->logger)
1212 config_->logger->debug("[device {}] Send request on TLS socket for channel {}",
1213 deviceId, name);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001214 sendChannelRequest(dinfo, info, info->socket_, name, id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001215 }
1216 }
1217}
1218
1219void
1220ConnectionManager::Impl::answerTo(IceTransport& ice,
1221 const dht::Value::Id& id,
1222 const std::shared_ptr<dht::crypto::PublicKey>& from)
1223{
1224 // NOTE: This is a shortest version of a real SDP message to save some bits
1225 auto iceAttributes = ice.getLocalAttributes();
1226 std::ostringstream icemsg;
1227 icemsg << iceAttributes.ufrag << "\n";
1228 icemsg << iceAttributes.pwd << "\n";
1229 for (const auto& addr : ice.getLocalCandidates(1)) {
1230 icemsg << addr << "\n";
1231 }
1232
1233 // Send PeerConnection response
1234 PeerConnectionRequest val;
1235 val.id = id;
1236 val.ice_msg = icemsg.str();
1237 val.isAnswer = true;
1238 auto value = std::make_shared<dht::Value>(std::move(val));
1239 value->user_type = "peer_request";
1240
1241 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001242 config_->logger->debug("[device {}] Connection accepted, DHT reply", from->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001243 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
1244 + from->getId().toString()),
1245 from,
1246 value,
1247 [from,l=config_->logger](bool ok) {
1248 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -04001249 l->debug("[device {}] Answer to connection request: put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001250 from->getLongId(),
1251 (ok ? "ok" : "failed"));
1252 });
1253}
1254
1255bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001256ConnectionManager::Impl::onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001257{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001258 if (!info)
1259 return false;
1260
Adrien Béraud75754b22023-10-17 09:16:06 -04001261 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001262 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001263 auto& ice = info->ice_;
1264 if (!ice) {
1265 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001266 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001267 if (connReadyCb_)
1268 connReadyCb_(deviceId, "", nullptr);
1269 return false;
1270 }
1271
1272 auto sdp = ice->parseIceCandidates(req.ice_msg);
1273 answerTo(*ice, req.id, req.owner);
1274 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
1275 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001276 config_->logger->error("[device {}] Start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001277 ice = nullptr;
1278 if (connReadyCb_)
1279 connReadyCb_(deviceId, "", nullptr);
1280 return false;
1281 }
1282 return true;
1283}
1284
1285bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001286ConnectionManager::Impl::onRequestOnNegoDone(const std::weak_ptr<DeviceInfo>& dinfo, const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001287{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001288 if (!info)
1289 return false;
1290
Adrien Béraud75754b22023-10-17 09:16:06 -04001291 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001292 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001293 auto& ice = info->ice_;
1294 if (!ice) {
1295 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001296 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001297 return false;
1298 }
1299
1300 // Build socket
1301 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
1302 std::move(ice)),
1303 false);
1304
1305 // init TLS session
1306 auto ph = req.from;
1307 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001308 config_->logger->debug("[device {}] Start TLS session - Initied by DHT request. vid: {}",
1309 deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001310 req.id);
1311 info->tls_ = std::make_unique<TlsSocketEndpoint>(
1312 std::move(endpoint),
1313 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -04001314 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001315 identity(),
1316 dhParams(),
Adrien Béraud75754b22023-10-17 09:16:06 -04001317 [ph, deviceId, w=weak_from_this(), l=config_->logger](const dht::crypto::Certificate& cert) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001318 auto shared = w.lock();
1319 if (!shared)
1320 return false;
Adrien Béraud9efbd442023-08-27 12:38:07 -04001321 if (cert.getPublicKey().getId() != ph
1322 || deviceId != cert.getPublicKey().getLongId()) {
1323 if (l) l->warn("[device {}] TLS certificate with ID {} doesn't match the DHT request.",
1324 deviceId,
1325 cert.getPublicKey().getLongId());
1326 return false;
1327 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001328 auto crt = shared->certStore().getCertificate(cert.getLongId().toString());
1329 if (!crt)
1330 return false;
1331 return crt->getPacked() == cert.getPacked();
1332 });
1333
1334 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001335 [w = weak_from_this(), dinfo, winfo=std::weak_ptr(info), deviceId = std::move(deviceId), vid = std::move(req.id)](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001336 if (auto shared = w.lock())
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -05001337 if (auto info = winfo.lock()) {
1338 shared->onTlsNegotiationDone(dinfo.lock(), winfo.lock(), ok, deviceId, vid);
1339 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
1340 dht::ThreadPool::io().run([info = std::move(info)] {});
1341 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001342 });
1343 return true;
1344}
1345
1346void
1347ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
1348 const std::shared_ptr<dht::crypto::Certificate>& /*cert*/)
1349{
1350 auto deviceId = req.owner->getLongId();
1351 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001352 config_->logger->debug("[device {}] New connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001353 if (!iceReqCb_ || !iceReqCb_(deviceId)) {
1354 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001355 config_->logger->debug("[device {}] Refusing connection", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001356 return;
1357 }
1358
1359 // Because the connection is accepted, create an ICE socket.
Adrien Béraud75754b22023-10-17 09:16:06 -04001360 getIceOptions([w = weak_from_this(), req, deviceId](auto&& ice_config) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001361 auto shared = w.lock();
1362 if (!shared)
1363 return;
Adrien Béraud75754b22023-10-17 09:16:06 -04001364
1365 auto di = shared->infos_.createDeviceInfo(deviceId);
1366 auto info = std::make_shared<ConnectionInfo>();
1367 auto wdi = std::weak_ptr(di);
1368 auto winfo = std::weak_ptr(info);
1369
Adrien Béraud612b55b2023-05-29 10:42:04 -04001370 // Note: used when the ice negotiation fails to erase
1371 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -04001372 auto eraseInfo = [w, wdi, id = req.id] {
1373 auto shared = w.lock();
1374 if (auto di = wdi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001375 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001376 di->info.erase(id);
1377 auto ops = di->extractPendingOperations(id, nullptr);
1378 if (di->empty()) {
1379 if (shared)
1380 shared->infos_.removeDeviceInfo(di->deviceId);
1381 }
1382 lk.unlock();
1383 for (const auto& op: ops)
1384 op.cb(nullptr, di->deviceId);
1385 if (shared && shared->connReadyCb_)
1386 shared->connReadyCb_(di->deviceId, "", nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001387 }
1388 };
1389
Adrien Béraud75754b22023-10-17 09:16:06 -04001390 ice_config.master = true;
1391 ice_config.streamsCount = 1;
1392 ice_config.compCountPerStream = 1; // TCP
Adrien Béraud612b55b2023-05-29 10:42:04 -04001393 ice_config.tcpEnable = true;
Adrien Béraud75754b22023-10-17 09:16:06 -04001394 ice_config.onInitDone = [w, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001395 auto shared = w.lock();
1396 if (!shared)
1397 return;
1398 if (!ok) {
1399 if (shared->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001400 shared->config_->logger->error("[device {}] Unable to initialize ICE session.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001401 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1402 return;
1403 }
1404
1405 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001406 [w = std::move(w), winfo = std::move(winfo), req = std::move(req), eraseInfo = std::move(eraseInfo)] {
1407 if (auto shared = w.lock()) {
1408 if (!shared->onRequestStartIce(winfo.lock(), req))
1409 eraseInfo();
1410 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001411 });
1412 };
1413
Adrien Béraud75754b22023-10-17 09:16:06 -04001414 ice_config.onNegoDone = [w, wdi, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001415 auto shared = w.lock();
1416 if (!shared)
1417 return;
1418 if (!ok) {
1419 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001420 shared->config_->logger->error("[device {}] ICE negotiation failed.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001421 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1422 return;
1423 }
1424
1425 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001426 [w = std::move(w), wdi = std::move(wdi), winfo = std::move(winfo), req = std::move(req), eraseInfo = std::move(eraseInfo)] {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001427 if (auto shared = w.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001428 if (!shared->onRequestOnNegoDone(wdi.lock(), winfo.lock(), req))
Adrien Béraud612b55b2023-05-29 10:42:04 -04001429 eraseInfo();
1430 });
1431 };
1432
1433 // Negotiate a new ICE socket
Adrien Béraud612b55b2023-05-29 10:42:04 -04001434 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001435 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001436 di->info[req.id] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001437 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001438
Adrien Béraud612b55b2023-05-29 10:42:04 -04001439 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001440 shared->config_->logger->debug("[device {}] Accepting connection", deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001441 std::unique_lock lk {info->mutex_};
Sébastien Blin34086512023-07-25 09:52:14 -04001442 info->ice_ = shared->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001443 if (not info->ice_) {
1444 if (shared->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001445 shared->config_->logger->error("[device {}] Unable to initialize ICE session", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001446 eraseInfo();
1447 return;
1448 }
1449 // We need to detect any shutdown if the ice session is destroyed before going to the TLS session;
1450 info->ice_->setOnShutdown([eraseInfo]() {
1451 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1452 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001453 try {
1454 info->ice_->initIceInstance(ice_config);
1455 } catch (const std::exception& e) {
1456 if (shared->config_->logger)
1457 shared->config_->logger->error("{}", e.what());
1458 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1459 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001460 });
1461}
1462
1463void
Adrien Béraud75754b22023-10-17 09:16:06 -04001464ConnectionManager::Impl::addNewMultiplexedSocket(const std::weak_ptr<DeviceInfo>& dinfo, const DeviceId& deviceId, const dht::Value::Id& vid, const std::shared_ptr<ConnectionInfo>& info)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001465{
Adrien Béraud75754b22023-10-17 09:16:06 -04001466 info->socket_ = std::make_shared<MultiplexedSocket>(config_->ioContext, deviceId, std::move(info->tls_), config_->logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001467 info->socket_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001468 [w = weak_from_this()](const DeviceId& deviceId, const std::shared_ptr<ChannelSocket>& socket) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001469 if (auto sthis = w.lock())
1470 if (sthis->connReadyCb_)
1471 sthis->connReadyCb_(deviceId, socket->name(), socket);
1472 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001473 info->socket_->setOnRequest([w = weak_from_this()](const std::shared_ptr<dht::crypto::Certificate>& peer,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001474 const uint16_t&,
1475 const std::string& name) {
1476 if (auto sthis = w.lock())
1477 if (sthis->channelReqCb_)
1478 return sthis->channelReqCb_(peer, name);
1479 return false;
1480 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001481 info->socket_->onShutdown([dinfo, wi=std::weak_ptr(info), vid]() {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001482 // Cancel current outgoing connections
Adrien Béraud75754b22023-10-17 09:16:06 -04001483 dht::ThreadPool::io().run([dinfo, wi, vid] {
1484 std::set<dht::Value::Id> ids;
1485 if (auto info = wi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001486 std::lock_guard lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001487 if (info->socket_) {
1488 ids = std::move(info->cbIds_);
1489 info->socket_->shutdown();
1490 }
1491 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001492 if (auto deviceInfo = dinfo.lock()) {
1493 std::shared_ptr<ConnectionInfo> info;
1494 std::vector<PendingCb> ops;
Adrien Béraud024c46f2024-03-02 23:53:18 -05001495 std::unique_lock lk(deviceInfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001496 auto it = deviceInfo->info.find(vid);
1497 if (it != deviceInfo->info.end()) {
1498 info = std::move(it->second);
1499 deviceInfo->info.erase(it);
1500 }
1501 for (const auto& cbId : ids) {
1502 auto po = deviceInfo->extractPendingOperations(cbId, nullptr);
1503 ops.insert(ops.end(), po.begin(), po.end());
1504 }
1505 lk.unlock();
1506 for (auto& op : ops)
1507 op.cb(nullptr, deviceInfo->deviceId);
1508 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001509 });
1510 });
1511}
1512
1513const std::shared_future<tls::DhParams>
1514ConnectionManager::Impl::dhParams() const
1515{
1516 return dht::ThreadPool::computation().get<tls::DhParams>(
Adrien Béraud2a4e73d2023-08-27 12:53:55 -04001517 std::bind(tls::DhParams::loadDhParams, config_->cachePath / "dhParams"));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001518}
1519
Adrien Béraud612b55b2023-05-29 10:42:04 -04001520bool
Adrien Béraud5aec4102024-02-22 14:15:56 -05001521ConnectionManager::Impl::isMessageTreated(dht::Value::Id id)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001522{
Adrien Béraud024c46f2024-03-02 23:53:18 -05001523 std::lock_guard lock(messageMutex_);
Adrien Béraud28e2ca52024-02-23 14:19:59 -05001524 return !treatedMessages_.add(id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001525}
1526
Adrien Béraud612b55b2023-05-29 10:42:04 -04001527
1528IpAddr
1529ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
1530{
1531 if (family == AF_INET)
1532 return publishedIp_[0];
1533 if (family == AF_INET6)
1534 return publishedIp_[1];
1535
1536 assert(family == AF_UNSPEC);
1537
1538 // If family is not set, prefere IPv4 if available. It's more
1539 // likely to succeed behind NAT.
1540 if (publishedIp_[0])
1541 return publishedIp_[0];
1542 if (publishedIp_[1])
1543 return publishedIp_[1];
1544 return {};
1545}
1546
1547void
1548ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr)
1549{
1550 if (ip_addr.getFamily() == AF_INET) {
1551 publishedIp_[0] = ip_addr;
1552 } else {
1553 publishedIp_[1] = ip_addr;
1554 }
1555}
1556
1557void
1558ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb)
1559{
Adrien Béraud75754b22023-10-17 09:16:06 -04001560 dht()->getPublicAddress([w=weak_from_this(), cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
Sébastien Blinb6504372023-10-12 10:35:35 -04001561 auto shared = w.lock();
1562 if (!shared)
1563 return;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001564 bool hasIpv4 {false}, hasIpv6 {false};
1565 for (auto& result : results) {
1566 auto family = result.getFamily();
1567 if (family == AF_INET) {
1568 if (not hasIpv4) {
1569 hasIpv4 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001570 if (shared->config_->logger)
1571 shared->config_->logger->debug("Store DHT public IPv4 address: {}", result);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001572 //JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str());
Sébastien Blinb6504372023-10-12 10:35:35 -04001573 shared->setPublishedAddress(*result.get());
1574 if (shared->config_->upnpCtrl) {
1575 shared->config_->upnpCtrl->setPublicAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001576 }
1577 }
1578 } else if (family == AF_INET6) {
1579 if (not hasIpv6) {
1580 hasIpv6 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001581 if (shared->config_->logger)
1582 shared->config_->logger->debug("Store DHT public IPv6 address: {}", result);
1583 shared->setPublishedAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001584 }
1585 }
1586 if (hasIpv4 and hasIpv6)
1587 break;
1588 }
1589 if (cb)
1590 cb();
1591 });
1592}
1593
1594void
1595ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1596{
1597 storeActiveIpAddress([this, cb = std::move(cb)] {
1598 IceTransportOptions opts = ConnectionManager::Impl::getIceOptions();
1599 auto publishedAddr = getPublishedIpAddress();
1600
1601 if (publishedAddr) {
1602 auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
1603 publishedAddr.getFamily());
1604 if (interfaceAddr) {
1605 opts.accountLocalAddr = interfaceAddr;
1606 opts.accountPublicAddr = publishedAddr;
1607 }
1608 }
1609 if (cb)
1610 cb(std::move(opts));
1611 });
1612}
1613
1614IceTransportOptions
1615ConnectionManager::Impl::getIceOptions() const noexcept
1616{
1617 IceTransportOptions opts;
Sébastien Blin34086512023-07-25 09:52:14 -04001618 opts.factory = config_->factory;
Amna49da80f2024-08-26 16:54:40 -04001619 opts.upnpEnable = config_->upnpEnabled;
Adrien Béraud7b869d92023-08-21 09:02:35 -04001620 opts.upnpContext = config_->upnpCtrl ? config_->upnpCtrl->upnpContext() : nullptr;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001621
1622 if (config_->stunEnabled)
1623 opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer));
1624 if (config_->turnEnabled) {
Sébastien Blin84bf4182023-07-21 14:18:39 -04001625 if (config_->turnCache) {
1626 auto turnAddr = config_->turnCache->getResolvedTurn();
1627 if (turnAddr != std::nullopt) {
1628 opts.turnServers.emplace_back(TurnServerInfo()
1629 .setUri(turnAddr->toString())
1630 .setUsername(config_->turnServerUserName)
1631 .setPassword(config_->turnServerPwd)
1632 .setRealm(config_->turnServerRealm));
1633 }
1634 } else {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001635 opts.turnServers.emplace_back(TurnServerInfo()
Sébastien Blin84bf4182023-07-21 14:18:39 -04001636 .setUri(config_->turnServer)
1637 .setUsername(config_->turnServerUserName)
1638 .setPassword(config_->turnServerPwd)
1639 .setRealm(config_->turnServerRealm));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001640 }
1641 // NOTE: first test with ipv6 turn was not concluant and resulted in multiple
1642 // co issues. So this needs some debug. for now just disable
1643 // if (cacheTurnV6 && *cacheTurnV6) {
1644 // opts.turnServers.emplace_back(TurnServerInfo()
1645 // .setUri(cacheTurnV6->toString(true))
1646 // .setUsername(turnServerUserName_)
1647 // .setPassword(turnServerPwd_)
1648 // .setRealm(turnServerRealm_));
1649 //}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001650 }
1651 return opts;
1652}
1653
1654bool
1655ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
1656 dht::InfoHash& account_id,
1657 const std::shared_ptr<Logger>& logger)
1658{
1659 if (not crt)
1660 return false;
1661
1662 auto top_issuer = crt;
1663 while (top_issuer->issuer)
1664 top_issuer = top_issuer->issuer;
1665
ovari123a15c6882024-09-17 18:34:20 -04001666 // Unable to self-signed device certificate
Adrien Béraudc631a832023-07-26 22:19:00 -04001667 if (top_issuer == crt) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001668 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001669 logger->warn("Found invalid (self-signed) peer device: {}", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001670 return false;
Adrien Béraudc631a832023-07-26 22:19:00 -04001671 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001672
1673 // Check peer certificate chain
1674 // Trust store with top issuer as the only CA
1675 dht::crypto::TrustList peer_trust;
1676 peer_trust.add(*top_issuer);
1677 if (not peer_trust.verify(*crt)) {
1678 if (logger)
1679 logger->warn("Found invalid peer device: {}", crt->getLongId());
1680 return false;
1681 }
1682
1683 // Check cached OCSP response
1684 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
1685 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001686 logger->error("Certificate {} is disabled by cached OCSP response", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001687 return false;
1688 }
1689
Adrien Béraudc631a832023-07-26 22:19:00 -04001690 account_id = crt->issuer->getId();
1691 if (logger)
1692 logger->warn("Found peer device: {} account:{} CA:{}",
1693 crt->getLongId(),
1694 account_id,
1695 top_issuer->getId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001696 return true;
1697}
1698
1699bool
1700ConnectionManager::Impl::findCertificate(
1701 const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1702{
1703 if (auto cert = certStore().getCertificate(id.toString())) {
1704 if (cb)
1705 cb(cert);
1706 } else if (cb)
1707 cb(nullptr);
1708 return true;
1709}
1710
Sébastien Blin34086512023-07-25 09:52:14 -04001711bool
1712ConnectionManager::Impl::findCertificate(const dht::InfoHash& h,
1713 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1714{
1715 if (auto cert = certStore().getCertificate(h.toString())) {
1716 if (cb)
1717 cb(cert);
1718 } else {
1719 dht()->findCertificate(h,
1720 [cb = std::move(cb), this](
1721 const std::shared_ptr<dht::crypto::Certificate>& crt) {
1722 if (crt)
1723 certStore().pinCertificate(crt);
1724 if (cb)
1725 cb(crt);
1726 });
1727 }
1728 return true;
1729}
1730
Amna81221ad2023-09-14 17:33:26 -04001731std::shared_ptr<ConnectionManager::Config>
1732buildDefaultConfig(dht::crypto::Identity id){
1733 auto conf = std::make_shared<ConnectionManager::Config>();
1734 conf->id = std::move(id);
1735 return conf;
1736}
1737
Adrien Béraud612b55b2023-05-29 10:42:04 -04001738ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_)
1739 : pimpl_ {std::make_shared<Impl>(config_)}
1740{}
1741
Amna81221ad2023-09-14 17:33:26 -04001742ConnectionManager::ConnectionManager(dht::crypto::Identity id)
1743 : ConnectionManager {buildDefaultConfig(id)}
1744{}
1745
Adrien Béraud612b55b2023-05-29 10:42:04 -04001746ConnectionManager::~ConnectionManager()
1747{
1748 if (pimpl_)
1749 pimpl_->shutdown();
1750}
1751
1752void
1753ConnectionManager::connectDevice(const DeviceId& deviceId,
1754 const std::string& name,
1755 ConnectCallback cb,
1756 bool noNewSocket,
1757 bool forceNewSocket,
1758 const std::string& connType)
1759{
1760 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1761}
1762
1763void
Amna0cf544d2023-07-25 14:25:09 -04001764ConnectionManager::connectDevice(const dht::InfoHash& deviceId,
1765 const std::string& name,
1766 ConnectCallbackLegacy cb,
1767 bool noNewSocket,
1768 bool forceNewSocket,
1769 const std::string& connType)
1770{
1771 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1772}
1773
1774
1775void
Adrien Béraud612b55b2023-05-29 10:42:04 -04001776ConnectionManager::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
1777 const std::string& name,
1778 ConnectCallback cb,
1779 bool noNewSocket,
1780 bool forceNewSocket,
1781 const std::string& connType)
1782{
1783 pimpl_->connectDevice(cert, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1784}
1785
1786bool
1787ConnectionManager::isConnecting(const DeviceId& deviceId, const std::string& name) const
1788{
Adrien Béraud75754b22023-10-17 09:16:06 -04001789 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001790 std::unique_lock lk {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001791 return dinfo->isConnecting(name);
Adrien Béraud75754b22023-10-17 09:16:06 -04001792 }
1793 return false;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001794}
1795
Sébastien Blind0c92c72023-12-07 15:27:51 -05001796bool
1797ConnectionManager::isConnected(const DeviceId& deviceId) const
1798{
1799 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001800 std::unique_lock lk {dinfo->mtx_};
Sébastien Blind0c92c72023-12-07 15:27:51 -05001801 return dinfo->getConnectedInfo() != nullptr;
1802 }
1803 return false;
1804}
1805
Adrien Béraud612b55b2023-05-29 10:42:04 -04001806void
1807ConnectionManager::closeConnectionsWith(const std::string& peerUri)
1808{
Adrien Béraud75754b22023-10-17 09:16:06 -04001809 std::vector<std::shared_ptr<DeviceInfo>> dInfos;
1810 for (const auto& dinfo: pimpl_->infos_.getDeviceInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001811 std::unique_lock lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001812 bool isPeer = false;
1813 for (auto const& [id, cinfo]: dinfo->info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001814 std::lock_guard lkv {cinfo->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001815 auto tls = cinfo->tls_ ? cinfo->tls_.get() : (cinfo->socket_ ? cinfo->socket_->endpoint() : nullptr);
Adrien Béraudafa8e282023-09-24 12:53:20 -04001816 auto cert = tls ? tls->peerCertificate() : nullptr;
1817 if (not cert)
Adrien Béraud75754b22023-10-17 09:16:06 -04001818 cert = pimpl_->certStore().getCertificate(dinfo->deviceId.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001819 if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001820 isPeer = true;
1821 break;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001822 }
1823 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001824 lk.unlock();
1825 if (isPeer) {
1826 dInfos.emplace_back(std::move(dinfo));
1827 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001828 }
1829 // Stop connections to all peers devices
Adrien Béraud75754b22023-10-17 09:16:06 -04001830 for (const auto& dinfo : dInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001831 std::unique_lock lk {dinfo->mtx_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001832 auto unused = dinfo->extractUnusedConnections();
1833 auto pending = dinfo->extractPendingOperations(0, nullptr);
1834 pimpl_->infos_.removeDeviceInfo(dinfo->deviceId);
1835 lk.unlock();
1836 for (auto& op : unused)
1837 op->shutdown();
1838 for (auto& op : pending)
1839 op.cb(nullptr, dinfo->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001840 }
1841}
1842
1843void
1844ConnectionManager::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1845{
1846 pimpl_->onDhtConnected(devicePk);
1847}
1848
1849void
1850ConnectionManager::onICERequest(onICERequestCallback&& cb)
1851{
1852 pimpl_->iceReqCb_ = std::move(cb);
1853}
1854
1855void
1856ConnectionManager::onChannelRequest(ChannelRequestCallback&& cb)
1857{
1858 pimpl_->channelReqCb_ = std::move(cb);
1859}
1860
1861void
1862ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb)
1863{
1864 pimpl_->connReadyCb_ = std::move(cb);
1865}
1866
1867void
1868ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb)
1869{
1870 pimpl_->iOSConnectedCb_ = std::move(cb);
1871}
1872
1873std::size_t
1874ConnectionManager::activeSockets() const
1875{
Adrien Béraud75754b22023-10-17 09:16:06 -04001876 return pimpl_->infos_.getConnectedInfos().size();
Adrien Béraud612b55b2023-05-29 10:42:04 -04001877}
1878
1879void
1880ConnectionManager::monitor() const
1881{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001882 auto logger = pimpl_->config_->logger;
1883 if (!logger)
1884 return;
1885 logger->debug("ConnectionManager current status:");
Adrien Béraud75754b22023-10-17 09:16:06 -04001886 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001887 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001888 if (ci->socket_)
1889 ci->socket_->monitor();
1890 }
1891 logger->debug("ConnectionManager end status.");
1892}
1893
1894void
1895ConnectionManager::connectivityChanged()
1896{
Adrien Béraud75754b22023-10-17 09:16:06 -04001897 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001898 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001899 if (ci->socket_)
Adrien Béraud51a54712023-10-17 21:24:30 -04001900 dht::ThreadPool::io().run([s = ci->socket_] { s->sendBeacon(); });
Adrien Béraud612b55b2023-05-29 10:42:04 -04001901 }
1902}
1903
1904void
1905ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1906{
1907 return pimpl_->getIceOptions(std::move(cb));
1908}
1909
1910IceTransportOptions
1911ConnectionManager::getIceOptions() const noexcept
1912{
1913 return pimpl_->getIceOptions();
1914}
1915
1916IpAddr
1917ConnectionManager::getPublishedIpAddress(uint16_t family) const
1918{
1919 return pimpl_->getPublishedIpAddress(family);
1920}
1921
1922void
1923ConnectionManager::setPublishedAddress(const IpAddr& ip_addr)
1924{
1925 return pimpl_->setPublishedAddress(ip_addr);
1926}
1927
1928void
1929ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb)
1930{
1931 return pimpl_->storeActiveIpAddress(std::move(cb));
1932}
1933
1934std::shared_ptr<ConnectionManager::Config>
1935ConnectionManager::getConfig()
1936{
1937 return pimpl_->config_;
1938}
1939
Amna31791e52023-08-03 12:40:57 -04001940std::vector<std::map<std::string, std::string>>
1941ConnectionManager::getConnectionList(const DeviceId& device) const
1942{
1943 std::vector<std::map<std::string, std::string>> connectionsList;
Amna31791e52023-08-03 12:40:57 -04001944 if (device) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001945 if (auto deviceInfo = pimpl_->infos_.getDeviceInfo(device)) {
1946 connectionsList = deviceInfo->getConnectionList(pimpl_->certStore());
Amna31791e52023-08-03 12:40:57 -04001947 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001948 } else {
1949 for (const auto& deviceInfo : pimpl_->infos_.getDeviceInfos()) {
1950 auto cl = deviceInfo->getConnectionList(pimpl_->certStore());
1951 connectionsList.insert(connectionsList.end(), std::make_move_iterator(cl.begin()), std::make_move_iterator(cl.end()));
Amna31791e52023-08-03 12:40:57 -04001952 }
1953 }
1954 return connectionsList;
1955}
1956
1957std::vector<std::map<std::string, std::string>>
1958ConnectionManager::getChannelList(const std::string& connectionId) const
1959{
Adrien Béraud75754b22023-10-17 09:16:06 -04001960 auto [deviceId, valueId] = parseCallbackId(connectionId);
1961 if (auto info = pimpl_->infos_.getInfo(deviceId, valueId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001962 std::lock_guard lk(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001963 if (info->socket_)
1964 return info->socket_->getChannelList();
Amna31791e52023-08-03 12:40:57 -04001965 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001966 return {};
Amna31791e52023-08-03 12:40:57 -04001967}
1968
Sébastien Blin464bdff2023-07-19 08:02:53 -04001969} // namespace dhtnet