blob: 831762ba71da447d28031773e97954b58fd75407 [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 }
Adrien Béraudaca437e2024-09-23 16:40:34 -0400188 std::vector<std::shared_ptr<ConnectionInfo>> getConnectedInfos() const {
189 std::vector<std::shared_ptr<ConnectionInfo>> ret;
190 for (auto& [id, ci] : info) {
191 if (ci->socket_)
192 ret.emplace_back(ci);
193 }
194 return ret;
195 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400196
197 std::vector<PendingCb> extractPendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true)
198 {
199 std::vector<PendingCb> ret;
200 if (vid == 0) {
201 // Extract all pending callbacks
202 ret.reserve(connecting.size() + waiting.size());
203 for (auto& [vid, cb] : connecting)
204 ret.emplace_back(std::move(cb));
205 connecting.clear();
206 for (auto& [vid, cb] : waiting)
207 ret.emplace_back(std::move(cb));
208 waiting.clear();
209 } else if (auto n = waiting.extract(vid)) {
210 // If it's a waiting operation, just move it
211 ret.emplace_back(std::move(n.mapped()));
212 } else if (auto n = connecting.extract(vid)) {
213 ret.emplace_back(std::move(n.mapped()));
214 // If sock is nullptr, execute if it's the last connecting operation
215 // If accepted is false, it means that underlying socket is ok, but channel is declined
216 if (!sock && connecting.empty() && accepted) {
217 for (auto& [vid, cb] : waiting)
218 ret.emplace_back(std::move(cb));
219 waiting.clear();
220 for (auto& [vid, cb] : connecting)
221 ret.emplace_back(std::move(cb));
222 connecting.clear();
223 }
224 }
225 return ret;
226 }
227
228 std::vector<std::shared_ptr<ConnectionInfo>> extractUnusedConnections() {
229 std::vector<std::shared_ptr<ConnectionInfo>> unused {};
230 for (auto& [id, info] : info)
231 unused.emplace_back(std::move(info));
232 info.clear();
233 return unused;
234 }
235
236 void executePendingOperations(std::unique_lock<std::mutex>& lock, dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
237 auto ops = extractPendingOperations(vid, sock, accepted);
238 lock.unlock();
239 for (auto& cb : ops)
240 cb.cb(sock, deviceId);
241 }
242 void executePendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500243 std::unique_lock lock(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400244 executePendingOperations(lock, vid, sock, accepted);
245 }
246
Adrien Béraudb941e922023-10-16 12:56:14 -0400247 bool isConnecting(const std::string& name) const {
Adrien Béraud75754b22023-10-17 09:16:06 -0400248 for (const auto& [id, pc]: connecting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400249 if (pc.name == name)
250 return true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400251 for (const auto& [id, pc]: waiting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400252 if (pc.name == name)
253 return true;
254 return false;
255 }
256 std::map<dht::Value::Id, std::string> requestPendingOps() {
257 std::map<dht::Value::Id, std::string> ret;
258 for (auto& [id, pc]: connecting) {
259 if (!pc.requested) {
260 ret[id] = pc.name;
261 pc.requested = true;
262 }
263 }
264 for (auto& [id, pc]: waiting) {
265 if (!pc.requested) {
266 ret[id] = pc.name;
267 pc.requested = true;
268 }
269 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400270 return ret;
271 }
272
273 std::vector<std::map<std::string, std::string>>
274 getConnectionList(tls::CertificateStore& certStore) const {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500275 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400276 std::vector<std::map<std::string, std::string>> ret;
Adrien Béraudd5ec7a82023-10-28 18:07:03 -0400277 ret.reserve(info.size() + connecting.size() + waiting.size());
Adrien Béraud75754b22023-10-17 09:16:06 -0400278 for (auto& [id, ci] : info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500279 std::lock_guard lk(ci->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400280 ret.emplace_back(ci->getInfo(deviceId, id, certStore));
281 }
282 auto cert = certStore.getCertificate(deviceId.toString());
283 for (const auto& [vid, ci] : connecting) {
284 ret.emplace_back(std::map<std::string, std::string> {
285 {"id", callbackIdToString(deviceId, vid)},
286 {"status", std::to_string(static_cast<int>(ConnectionStatus::Connecting))},
287 {"device", deviceId.toString()},
288 {"peer", cert ? cert->issuer->getId().toString() : ""}
289 });
290 }
291 for (const auto& [vid, ci] : waiting) {
292 ret.emplace_back(std::map<std::string, std::string> {
293 {"id", callbackIdToString(deviceId, vid)},
294 {"status", std::to_string(static_cast<int>(ConnectionStatus::Waiting))},
295 {"device", deviceId.toString()},
296 {"peer", cert ? cert->issuer->getId().toString() : ""}
297 });
298 }
299 return ret;
300 }
301};
302
303class DeviceInfoSet {
304public:
305 std::shared_ptr<DeviceInfo> getDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500306 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400307 auto it = infos_.find(deviceId);
308 if (it != infos_.end())
309 return it->second;
310 return {};
311 }
312
313 std::vector<std::shared_ptr<DeviceInfo>> getDeviceInfos() {
314 std::vector<std::shared_ptr<DeviceInfo>> deviceInfos;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500315 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400316 deviceInfos.reserve(infos_.size());
317 for (auto& [deviceId, info] : infos_)
318 deviceInfos.emplace_back(info);
319 return deviceInfos;
320 }
321
322 std::shared_ptr<DeviceInfo> createDeviceInfo(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 auto& info = infos_[deviceId];
325 if (!info)
326 info = std::make_shared<DeviceInfo>(deviceId);
327 return info;
328 }
329
330 bool removeDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500331 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400332 return infos_.erase(deviceId) != 0;
333 }
334
335 std::shared_ptr<ConnectionInfo> getInfo(const DeviceId& deviceId, const dht::Value::Id& id) {
336 if (auto info = getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500337 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400338 auto it = info->info.find(id);
339 if (it != info->info.end())
340 return it->second;
341 }
342 return {};
343 }
344
345 std::vector<std::shared_ptr<ConnectionInfo>> getConnectedInfos() {
346 auto deviceInfos = getDeviceInfos();
347 std::vector<std::shared_ptr<ConnectionInfo>> ret;
348 ret.reserve(deviceInfos.size());
349 for (auto& info : deviceInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500350 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400351 for (auto& [id, ci] : info->info) {
352 if (ci->socket_)
353 ret.emplace_back(ci);
354 }
355 }
356 return ret;
357 }
358 std::vector<std::shared_ptr<DeviceInfo>> shutdown() {
359 std::vector<std::shared_ptr<DeviceInfo>> ret;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500360 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400361 ret.reserve(infos_.size());
362 for (auto& [deviceId, info] : infos_) {
363 ret.emplace_back(std::move(info));
364 }
365 infos_.clear();
366 return ret;
367 }
368
369private:
370 std::mutex mtx_ {};
371 std::map<DeviceId, std::shared_ptr<DeviceInfo>> infos_ {};
372};
373
Adrien Béraud612b55b2023-05-29 10:42:04 -0400374class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl>
375{
376public:
377 explicit Impl(std::shared_ptr<ConnectionManager::Config> config_)
Amna81221ad2023-09-14 17:33:26 -0400378 : config_ {std::move(createConfig(config_))}
Adrien Béraudd8b6a402023-12-08 14:19:25 -0500379 , rand_ {config_->rng ? *config_->rng : dht::crypto::getSeededRandomEngine<std::mt19937_64>()}
Adrien Béraud5aec4102024-02-22 14:15:56 -0500380 , treatedMessages_ {config_->cachePath / "treatedMessages"}
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400381 {
Amna81221ad2023-09-14 17:33:26 -0400382 if(!config_->ioContext) {
383 config_->ioContext = std::make_shared<asio::io_context>();
384 ioContextRunner_ = std::make_unique<std::thread>([context = config_->ioContext, l=config_->logger]() {
385 try {
386 auto work = asio::make_work_guard(*context);
387 context->run();
388 } catch (const std::exception& ex) {
389 if (l) l->error("Exception: {}", ex.what());
390 }
391 });
392 }
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400393 }
Amna81221ad2023-09-14 17:33:26 -0400394 ~Impl() {
395 if (ioContextRunner_) {
396 if (config_->logger) config_->logger->debug("ConnectionManager: stopping io_context thread");
397 config_->ioContext->stop();
398 ioContextRunner_->join();
399 ioContextRunner_.reset();
400 }
401 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400402
403 std::shared_ptr<dht::DhtRunner> dht() { return config_->dht; }
404 const dht::crypto::Identity& identity() const { return config_->id; }
405
Adrien Béraud75754b22023-10-17 09:16:06 -0400406 void shutdown()
Adrien Béraud612b55b2023-05-29 10:42:04 -0400407 {
Adrien Béraud75754b22023-10-17 09:16:06 -0400408 if (isDestroying_.exchange(true))
409 return;
410 std::vector<std::shared_ptr<ConnectionInfo>> unused;
411 std::vector<std::pair<DeviceId, std::vector<PendingCb>>> pending;
412 for (auto& dinfo: infos_.shutdown()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500413 std::lock_guard lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400414 auto p = dinfo->extractPendingOperations(0, nullptr, false);
415 if (!p.empty())
416 pending.emplace_back(dinfo->deviceId, std::move(p));
417 auto uc = dinfo->extractUnusedConnections();
418 unused.insert(unused.end(), std::make_move_iterator(uc.begin()), std::make_move_iterator(uc.end()));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400419 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400420 for (auto& info: unused)
421 info->shutdown();
422 for (auto& op: pending)
423 for (auto& cb: op.second)
424 cb.cb(nullptr, op.first);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400425 if (!unused.empty())
Amna81221ad2023-09-14 17:33:26 -0400426 dht::ThreadPool::io().run([infos = std::move(unused)]() mutable {
427 infos.clear();
428 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400429 }
430
Adrien Béraud75754b22023-10-17 09:16:06 -0400431 void connectDeviceStartIce(const std::shared_ptr<ConnectionInfo>& info,
432 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400433 const dht::Value::Id& vid,
434 const std::string& connType,
435 std::function<void(bool)> onConnected);
Adrien Béraud75754b22023-10-17 09:16:06 -0400436 void onResponse(const asio::error_code& ec, const std::weak_ptr<ConnectionInfo>& info, const DeviceId& deviceId, const dht::Value::Id& vid);
437 bool connectDeviceOnNegoDone(const std::weak_ptr<DeviceInfo>& dinfo,
438 const std::shared_ptr<ConnectionInfo>& info,
439 const DeviceId& deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400440 const std::string& name,
441 const dht::Value::Id& vid,
442 const std::shared_ptr<dht::crypto::Certificate>& cert);
443 void connectDevice(const DeviceId& deviceId,
444 const std::string& uri,
445 ConnectCallback cb,
446 bool noNewSocket = false,
447 bool forceNewSocket = false,
448 const std::string& connType = "");
Amna0cf544d2023-07-25 14:25:09 -0400449 void connectDevice(const dht::InfoHash& deviceId,
450 const std::string& uri,
451 ConnectCallbackLegacy cb,
452 bool noNewSocket = false,
453 bool forceNewSocket = false,
454 const std::string& connType = "");
455
Adrien Béraud612b55b2023-05-29 10:42:04 -0400456 void connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
457 const std::string& name,
458 ConnectCallback cb,
459 bool noNewSocket = false,
460 bool forceNewSocket = false,
461 const std::string& connType = "");
462 /**
463 * Send a ChannelRequest on the TLS socket. Triggers cb when ready
464 * @param sock socket used to send the request
465 * @param name channel's name
466 * @param vid channel's id
467 * @param deviceId to identify the linked ConnectCallback
468 */
Adrien Béraud75754b22023-10-17 09:16:06 -0400469 void sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfo,
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400470 const std::weak_ptr<ConnectionInfo>& cinfo,
Adrien Béraud75754b22023-10-17 09:16:06 -0400471 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400472 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400473 const dht::Value::Id& vid);
474 /**
475 * Triggered when a PeerConnectionRequest comes from the DHT
476 */
477 void answerTo(IceTransport& ice,
478 const dht::Value::Id& id,
479 const std::shared_ptr<dht::crypto::PublicKey>& fromPk);
Adrien Béraud75754b22023-10-17 09:16:06 -0400480 bool onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req);
481 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 -0400482 void onDhtPeerRequest(const PeerConnectionRequest& req,
483 const std::shared_ptr<dht::crypto::Certificate>& cert);
Adrien Béraud75754b22023-10-17 09:16:06 -0400484 /**
485 * Triggered when a new TLS socket is ready to use
486 * @param ok If succeed
487 * @param deviceId Related device
488 * @param vid vid of the connection request
489 * @param name non empty if TLS was created by connectDevice()
490 */
491 void onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
492 const std::shared_ptr<ConnectionInfo>& info,
493 bool ok,
494 const DeviceId& deviceId,
495 const dht::Value::Id& vid,
496 const std::string& name = "");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400497
Adrien Béraud75754b22023-10-17 09:16:06 -0400498 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 -0400499 void onPeerResponse(PeerConnectionRequest&& req);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400500 void onDhtConnected(const dht::crypto::PublicKey& devicePk);
501
Adrien Béraud75754b22023-10-17 09:16:06 -0400502
Adrien Béraud612b55b2023-05-29 10:42:04 -0400503 const std::shared_future<tls::DhParams> dhParams() const;
504 tls::CertificateStore& certStore() const { return *config_->certStore; }
505
506 mutable std::mutex messageMutex_ {};
Adrien Béraud5aec4102024-02-22 14:15:56 -0500507 fileutils::IdList treatedMessages_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400508
509 /// \return true if the given DHT message identifier has been treated
510 /// \note if message has not been treated yet this method st/ore this id and returns true at
511 /// further calls
Adrien Béraud5aec4102024-02-22 14:15:56 -0500512 bool isMessageTreated(dht::Value::Id id);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400513
514 const std::shared_ptr<dht::log::Logger>& logger() const { return config_->logger; }
515
516 /**
517 * Published IPv4/IPv6 addresses, used only if defined by the user in account
518 * configuration
519 *
520 */
521 IpAddr publishedIp_[2] {};
522
Adrien Béraud612b55b2023-05-29 10:42:04 -0400523 /**
524 * interface name on which this account is bound
525 */
526 std::string interface_ {"default"};
527
528 /**
529 * Get the local interface name on which this account is bound.
530 */
531 const std::string& getLocalInterface() const { return interface_; }
532
533 /**
534 * Get the published IP address, fallbacks to NAT if family is unspecified
535 * Prefers the usage of IPv4 if possible.
536 */
537 IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
538
539 /**
540 * Set published IP address according to given family
541 */
542 void setPublishedAddress(const IpAddr& ip_addr);
543
544 /**
545 * Store the local/public addresses used to register
546 */
547 void storeActiveIpAddress(std::function<void()>&& cb = {});
548
549 /**
550 * Create and return ICE options.
551 */
552 void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
553 IceTransportOptions getIceOptions() const noexcept;
554
555 /**
556 * Inform that a potential peer device have been found.
557 * Returns true only if the device certificate is a valid device certificate.
558 * In that case (true is returned) the account_id parameter is set to the peer account ID.
559 */
560 static bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
561 dht::InfoHash& account_id, const std::shared_ptr<Logger>& logger);
562
563 bool findCertificate(const dht::PkId& id,
564 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
Sébastien Blin34086512023-07-25 09:52:14 -0400565 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 -0400566
Adrien Béraud612b55b2023-05-29 10:42:04 -0400567 std::shared_ptr<ConnectionManager::Config> config_;
Amna81221ad2023-09-14 17:33:26 -0400568 std::unique_ptr<std::thread> ioContextRunner_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400569
Adrien Béraud75754b22023-10-17 09:16:06 -0400570 mutable std::mutex randMtx_;
571 mutable std::mt19937_64 rand_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400572
573 iOSConnectedCallback iOSConnectedCb_ {};
574
Adrien Béraud75754b22023-10-17 09:16:06 -0400575 DeviceInfoSet infos_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400576
577 ChannelRequestCallback channelReqCb_ {};
578 ConnectionReadyCallback connReadyCb_ {};
579 onICERequestCallback iceReqCb_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400580 std::atomic_bool isDestroying_ {false};
581};
582
583void
584ConnectionManager::Impl::connectDeviceStartIce(
Adrien Béraud75754b22023-10-17 09:16:06 -0400585 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400586 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
587 const dht::Value::Id& vid,
588 const std::string& connType,
589 std::function<void(bool)> onConnected)
590{
591 auto deviceId = devicePk->getLongId();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400592 if (!info) {
593 onConnected(false);
594 return;
595 }
596
Adrien Béraud024c46f2024-03-02 23:53:18 -0500597 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400598 auto& ice = info->ice_;
599
600 if (!ice) {
601 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400602 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400603 onConnected(false);
604 return;
605 }
606
607 auto iceAttributes = ice->getLocalAttributes();
608 std::ostringstream icemsg;
609 icemsg << iceAttributes.ufrag << "\n";
610 icemsg << iceAttributes.pwd << "\n";
611 for (const auto& addr : ice->getLocalCandidates(1)) {
612 icemsg << addr << "\n";
613 if (config_->logger)
Sébastien Blinaec46fc2023-07-25 15:43:10 -0400614 config_->logger->debug("[device {}] Added local ICE candidate {}", deviceId, addr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400615 }
616
617 // Prepare connection request as a DHT message
618 PeerConnectionRequest val;
619
620 val.id = vid; /* Random id for the message unicity */
621 val.ice_msg = icemsg.str();
622 val.connType = connType;
623
624 auto value = std::make_shared<dht::Value>(std::move(val));
625 value->user_type = "peer_request";
626
627 // Send connection request through DHT
628 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400629 config_->logger->debug("[device {}] Sending connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400630 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
631 + devicePk->getId().toString()),
632 devicePk,
633 value,
634 [l=config_->logger,deviceId](bool ok) {
635 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -0400636 l->debug("[device {}] Sent connection request. Put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -0400637 deviceId,
638 (ok ? "ok" : "failed"));
639 });
640 // Wait for call to onResponse() operated by DHT
641 if (isDestroying_) {
642 onConnected(true); // This avoid to wait new negotiation when destroying
643 return;
644 }
645
646 info->onConnected_ = std::move(onConnected);
647 info->waitForAnswer_ = std::make_unique<asio::steady_timer>(*config_->ioContext,
648 std::chrono::steady_clock::now()
649 + DHT_MSG_TIMEOUT);
650 info->waitForAnswer_->async_wait(
Adrien Béraud75754b22023-10-17 09:16:06 -0400651 std::bind(&ConnectionManager::Impl::onResponse, this, std::placeholders::_1, info, deviceId, vid));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400652}
653
654void
655ConnectionManager::Impl::onResponse(const asio::error_code& ec,
Adrien Béraud75754b22023-10-17 09:16:06 -0400656 const std::weak_ptr<ConnectionInfo>& winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400657 const DeviceId& deviceId,
658 const dht::Value::Id& vid)
659{
660 if (ec == asio::error::operation_aborted)
661 return;
Adrien Béraud75754b22023-10-17 09:16:06 -0400662 auto info = winfo.lock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400663 if (!info)
664 return;
665
Adrien Béraud024c46f2024-03-02 23:53:18 -0500666 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400667 auto& ice = info->ice_;
668 if (isDestroying_) {
669 info->onConnected_(true); // The destructor can wake a pending wait here.
670 return;
671 }
672 if (!info->responseReceived_) {
673 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400674 config_->logger->error("[device {}] no response from DHT to ICE request.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400675 info->onConnected_(false);
676 return;
677 }
678
679 if (!info->ice_) {
680 info->onConnected_(false);
681 return;
682 }
683
684 auto sdp = ice->parseIceCandidates(info->response_.ice_msg);
685
686 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
687 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400688 config_->logger->warn("[device {}] start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400689 info->onConnected_(false);
690 return;
691 }
692 info->onConnected_(true);
693}
694
695bool
696ConnectionManager::Impl::connectDeviceOnNegoDone(
Adrien Béraud75754b22023-10-17 09:16:06 -0400697 const std::weak_ptr<DeviceInfo>& dinfo,
698 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400699 const DeviceId& deviceId,
700 const std::string& name,
701 const dht::Value::Id& vid,
702 const std::shared_ptr<dht::crypto::Certificate>& cert)
703{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400704 if (!info)
705 return false;
706
Adrien Béraud024c46f2024-03-02 23:53:18 -0500707 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400708 if (info->waitForAnswer_) {
709 // Negotiation is done and connected, go to handshake
710 // and avoid any cancellation at this point.
711 info->waitForAnswer_->cancel();
712 }
713 auto& ice = info->ice_;
714 if (!ice || !ice->isRunning()) {
715 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400716 config_->logger->error("[device {}] No ICE detected or not running", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400717 return false;
718 }
719
720 // Build socket
721 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
722 std::move(ice)),
723 true);
724
725 // Negotiate a TLS session
726 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400727 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 -0400728 info->tls_ = std::make_unique<TlsSocketEndpoint>(std::move(endpoint),
729 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400730 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400731 identity(),
732 dhParams(),
733 *cert);
734
735 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -0400736 [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 -0400737 bool ok) {
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500738 if (auto shared = w.lock())
Andreas Traczykb23278d2023-12-11 16:14:00 -0500739 if (auto info = winfo.lock()) {
740 shared->onTlsNegotiationDone(dinfo.lock(), info, ok, deviceId, vid, name);
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500741 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
Andreas Traczykb23278d2023-12-11 16:14:00 -0500742 dht::ThreadPool::io().run([info = std::move(info)] {});
743 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400744 });
745 return true;
746}
747
748void
749ConnectionManager::Impl::connectDevice(const DeviceId& deviceId,
750 const std::string& name,
751 ConnectCallback cb,
752 bool noNewSocket,
753 bool forceNewSocket,
754 const std::string& connType)
755{
756 if (!dht()) {
757 cb(nullptr, deviceId);
758 return;
759 }
760 if (deviceId.toString() == identity().second->getLongId().toString()) {
761 cb(nullptr, deviceId);
762 return;
763 }
764 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400765 [w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400766 deviceId,
767 name,
768 cb = std::move(cb),
769 noNewSocket,
770 forceNewSocket,
771 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
772 if (!cert) {
773 if (auto shared = w.lock())
774 if (shared->config_->logger)
775 shared->config_->logger->error(
776 "No valid certificate found for device {}",
777 deviceId);
778 cb(nullptr, deviceId);
779 return;
780 }
781 if (auto shared = w.lock()) {
782 shared->connectDevice(cert,
783 name,
784 std::move(cb),
785 noNewSocket,
786 forceNewSocket,
787 connType);
788 } else
789 cb(nullptr, deviceId);
790 });
791}
792
793void
Amna0cf544d2023-07-25 14:25:09 -0400794ConnectionManager::Impl::connectDevice(const dht::InfoHash& deviceId,
795 const std::string& name,
796 ConnectCallbackLegacy cb,
797 bool noNewSocket,
798 bool forceNewSocket,
799 const std::string& connType)
800{
801 if (!dht()) {
802 cb(nullptr, deviceId);
803 return;
804 }
805 if (deviceId.toString() == identity().second->getLongId().toString()) {
806 cb(nullptr, deviceId);
807 return;
808 }
809 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400810 [w = weak_from_this(),
Amna0cf544d2023-07-25 14:25:09 -0400811 deviceId,
812 name,
813 cb = std::move(cb),
814 noNewSocket,
815 forceNewSocket,
816 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
817 if (!cert) {
818 if (auto shared = w.lock())
819 if (shared->config_->logger)
820 shared->config_->logger->error(
821 "No valid certificate found for device {}",
822 deviceId);
823 cb(nullptr, deviceId);
824 return;
825 }
826 if (auto shared = w.lock()) {
827 shared->connectDevice(cert,
828 name,
Adrien Béraudd78d1ac2023-08-25 10:43:33 -0400829 [cb, deviceId](const std::shared_ptr<ChannelSocket>& sock, const DeviceId& /*did*/){
Amna0cf544d2023-07-25 14:25:09 -0400830 cb(sock, deviceId);
831 },
832 noNewSocket,
833 forceNewSocket,
834 connType);
835 } else
836 cb(nullptr, deviceId);
837 });
838}
839
840void
Adrien Béraud612b55b2023-05-29 10:42:04 -0400841ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
842 const std::string& name,
843 ConnectCallback cb,
844 bool noNewSocket,
845 bool forceNewSocket,
846 const std::string& connType)
847{
848 // Avoid dht operation in a DHT callback to avoid deadlocks
Adrien Béraud75754b22023-10-17 09:16:06 -0400849 dht::ThreadPool::computation().run([w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400850 name = std::move(name),
851 cert = std::move(cert),
852 cb = std::move(cb),
853 noNewSocket,
854 forceNewSocket,
855 connType] {
856 auto devicePk = cert->getSharedPublicKey();
857 auto deviceId = devicePk->getLongId();
858 auto sthis = w.lock();
859 if (!sthis || sthis->isDestroying_) {
860 cb(nullptr, deviceId);
861 return;
862 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400863 auto di = sthis->infos_.createDeviceInfo(deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -0500864 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400865
Adrien Béraud26365c92023-09-23 23:42:43 -0400866 dht::Value::Id vid;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400867 {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500868 std::lock_guard lkr(sthis->randMtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400869 vid = di->newId(sthis->rand_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400870 }
871
Adrien Béraud75754b22023-10-17 09:16:06 -0400872 // Check if already connecting
873 auto isConnectingToDevice = di->isConnecting();
874 // Note: we can be in a state where first
875 // socket is negotiated and first channel is pending
876 // so return only after we checked the info
Adrien Béraudb941e922023-10-16 12:56:14 -0400877 auto& diw = (isConnectingToDevice && !forceNewSocket)
878 ? di->waiting[vid]
879 : di->connecting[vid];
880 diw = PendingCb {name, std::move(cb)};
881
Adrien Béraud612b55b2023-05-29 10:42:04 -0400882 // Check if already negotiated
Adrien Béraud75754b22023-10-17 09:16:06 -0400883 if (auto info = di->getConnectedInfo()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500884 std::unique_lock lkc(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400885 if (auto sock = info->socket_) {
Adrien Béraud1aaaa962024-09-26 12:41:58 -0400886 if (sock->isRunning()) {
887 info->cbIds_.emplace(vid);
888 diw.requested = true;
889 lkc.unlock();
890 lk.unlock();
891 if (sthis->config_->logger)
892 sthis->config_->logger->debug("[device {}] Peer already connected. Add a new channel", deviceId);
893 sthis->sendChannelRequest(di, info, sock, name, vid);
894 return;
895 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400896 }
897 }
898
899 if (isConnectingToDevice && !forceNewSocket) {
900 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400901 sthis->config_->logger->debug("[device {}] Already connecting, wait for ICE negotiation", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400902 return;
903 }
904 if (noNewSocket) {
905 // If no new socket is specified, we don't try to generate a new socket
Adrien Béraud75754b22023-10-17 09:16:06 -0400906 di->executePendingOperations(lk, vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400907 return;
908 }
909
910 // Note: used when the ice negotiation fails to erase
911 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -0400912 auto eraseInfo = [w, diw=std::weak_ptr(di), vid] {
913 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500914 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400915 di->info.erase(vid);
916 auto ops = di->extractPendingOperations(vid, nullptr);
917 if (di->empty()) {
918 if (auto shared = w.lock())
919 shared->infos_.removeDeviceInfo(di->deviceId);
920 }
921 lk.unlock();
922 for (const auto& op: ops)
923 op.cb(nullptr, di->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400924 }
925 };
926
927 // If no socket exists, we need to initiate an ICE connection.
928 sthis->getIceOptions([w,
929 deviceId = std::move(deviceId),
930 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400931 diw=std::weak_ptr(di),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400932 name = std::move(name),
933 cert = std::move(cert),
934 vid,
935 connType,
936 eraseInfo](auto&& ice_config) {
937 auto sthis = w.lock();
938 if (!sthis) {
939 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
940 return;
941 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400942 auto info = std::make_shared<ConnectionInfo>();
943 auto winfo = std::weak_ptr(info);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400944 ice_config.tcpEnable = true;
945 ice_config.onInitDone = [w,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400946 devicePk = std::move(devicePk),
947 name = std::move(name),
948 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400949 diw,
950 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400951 vid,
952 connType,
953 eraseInfo](bool ok) {
954 dht::ThreadPool::io().run([w = std::move(w),
955 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400956 vid,
957 winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400958 eraseInfo,
959 connType, ok] {
960 auto sthis = w.lock();
961 if (!ok && sthis && sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -0400962 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", devicePk->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400963 if (!sthis || !ok) {
964 eraseInfo();
965 return;
966 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400967 sthis->connectDeviceStartIce(winfo.lock(), devicePk, vid, connType, [=](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400968 if (!ok) {
969 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
970 }
971 });
972 });
973 };
974 ice_config.onNegoDone = [w,
975 deviceId,
976 name,
977 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400978 diw,
979 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400980 vid,
981 eraseInfo](bool ok) {
982 dht::ThreadPool::io().run([w = std::move(w),
983 deviceId = std::move(deviceId),
984 name = std::move(name),
985 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400986 diw = std::move(diw),
987 winfo = std::move(winfo),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400988 vid = std::move(vid),
989 eraseInfo = std::move(eraseInfo),
990 ok] {
991 auto sthis = w.lock();
992 if (!ok && sthis && sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400993 sthis->config_->logger->error("[device {}] ICE negotiation failed.", deviceId);
Adrien Béraud75754b22023-10-17 09:16:06 -0400994 if (!sthis || !ok || !sthis->connectDeviceOnNegoDone(diw, winfo.lock(), deviceId, name, vid, cert))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400995 eraseInfo();
996 });
997 };
998
Adrien Béraud75754b22023-10-17 09:16:06 -0400999 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001000 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001001 di->info[vid] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001002 }
Adrien Béraud024c46f2024-03-02 23:53:18 -05001003 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001004 ice_config.master = false;
1005 ice_config.streamsCount = 1;
1006 ice_config.compCountPerStream = 1;
Sébastien Blin34086512023-07-25 09:52:14 -04001007 info->ice_ = sthis->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001008 if (!info->ice_) {
1009 if (sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001010 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001011 eraseInfo();
1012 return;
1013 }
1014 // We need to detect any shutdown if the ice session is destroyed before going to the
1015 // TLS session;
1016 info->ice_->setOnShutdown([eraseInfo]() {
1017 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1018 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001019 try {
1020 info->ice_->initIceInstance(ice_config);
1021 } catch (const std::exception& e) {
1022 if (sthis->config_->logger)
1023 sthis->config_->logger->error("{}", e.what());
1024 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1025 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001026 });
1027 });
1028}
1029
1030void
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001031ConnectionManager::Impl::sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfow,
1032 const std::weak_ptr<ConnectionInfo>& cinfow,
Adrien Béraud75754b22023-10-17 09:16:06 -04001033 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001034 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001035 const dht::Value::Id& vid)
1036{
1037 auto channelSock = sock->addChannel(name);
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001038 if (!channelSock) {
1039 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001040 config_->logger->error("sendChannelRequest failed - unable to create channel");
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001041 if (auto info = dinfow.lock())
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001042 info->executePendingOperations(vid, nullptr);
1043 return;
1044 }
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001045 channelSock->onShutdown([dinfow, name, vid] {
1046 if (auto info = dinfow.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001047 info->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001048 });
1049 channelSock->onReady(
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001050 [dinfow, cinfow, wSock = std::weak_ptr(channelSock), name, vid](bool accepted) {
1051 if (auto dinfo = dinfow.lock()) {
1052 dinfo->executePendingOperations(vid, accepted ? wSock.lock() : nullptr, accepted);
Sébastien Blinad161572024-01-31 14:14:51 -05001053 // Always lock top-down cinfo->mutex
1054 dht::ThreadPool::io().run([cinfow, vid]() {
1055 if (auto cinfo = cinfow.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001056 std::lock_guard lk(cinfo->mutex_);
Sébastien Blinad161572024-01-31 14:14:51 -05001057 cinfo->cbIds_.erase(vid);
1058 }
1059 });
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001060 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001061 });
1062
1063 ChannelRequest val;
1064 val.name = channelSock->name();
1065 val.state = ChannelRequestState::REQUEST;
1066 val.channel = channelSock->channel();
1067 msgpack::sbuffer buffer(256);
1068 msgpack::pack(buffer, val);
1069
1070 std::error_code ec;
1071 int res = sock->write(CONTROL_CHANNEL,
1072 reinterpret_cast<const uint8_t*>(buffer.data()),
1073 buffer.size(),
1074 ec);
1075 if (res < 0) {
1076 // TODO check if we should handle errors here
1077 if (config_->logger)
Adrien Béraud75754b22023-10-17 09:16:06 -04001078 config_->logger->error("sendChannelRequest failed - error: {}", ec.message());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001079 }
1080}
1081
1082void
Adrien Béraud1addf952023-09-30 17:38:35 -04001083ConnectionManager::Impl::onPeerResponse(PeerConnectionRequest&& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001084{
1085 auto device = req.owner->getLongId();
Adrien Béraud75754b22023-10-17 09:16:06 -04001086 if (auto info = infos_.getInfo(device, req.id)) {
Adrien Béraud23852462023-07-22 01:46:27 -04001087 if (config_->logger)
1088 config_->logger->debug("[device {}] New response received", device);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001089 std::lock_guard lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001090 info->responseReceived_ = true;
1091 info->response_ = std::move(req);
1092 info->waitForAnswer_->expires_at(std::chrono::steady_clock::now());
1093 info->waitForAnswer_->async_wait(std::bind(&ConnectionManager::Impl::onResponse,
1094 this,
1095 std::placeholders::_1,
Adrien Béraud75754b22023-10-17 09:16:06 -04001096 std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -04001097 device,
1098 req.id));
1099 } else {
1100 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001101 config_->logger->warn("[device {}] Response received, but unable to find request", device);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001102 }
1103}
1104
1105void
1106ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1107{
1108 if (!dht())
1109 return;
1110 dht()->listen<PeerConnectionRequest>(
1111 dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()),
Adrien Béraud75754b22023-10-17 09:16:06 -04001112 [w = weak_from_this()](PeerConnectionRequest&& req) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001113 auto shared = w.lock();
1114 if (!shared)
1115 return false;
Adrien Béraud5aec4102024-02-22 14:15:56 -05001116 if (shared->isMessageTreated(req.id)) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001117 // Message already treated. Just ignore
1118 return true;
1119 }
1120 if (req.isAnswer) {
1121 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001122 shared->config_->logger->debug("[device {}] Received request answer", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001123 } else {
1124 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001125 shared->config_->logger->debug("[device {}] Received request", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001126 }
1127 if (req.isAnswer) {
Adrien Béraud1addf952023-09-30 17:38:35 -04001128 shared->onPeerResponse(std::move(req));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001129 } else {
1130 // Async certificate checking
Sébastien Blin34086512023-07-25 09:52:14 -04001131 shared->findCertificate(
Adrien Béraud612b55b2023-05-29 10:42:04 -04001132 req.from,
1133 [w, req = std::move(req)](
1134 const std::shared_ptr<dht::crypto::Certificate>& cert) mutable {
1135 auto shared = w.lock();
1136 if (!shared)
1137 return;
1138 dht::InfoHash peer_h;
1139 if (foundPeerDevice(cert, peer_h, shared->config_->logger)) {
1140#if TARGET_OS_IOS
1141 if (shared->iOSConnectedCb_(req.connType, peer_h))
1142 return;
1143#endif
1144 shared->onDhtPeerRequest(req, cert);
1145 } else {
1146 if (shared->config_->logger)
1147 shared->config_->logger->warn(
Adrien Béraud23852462023-07-22 01:46:27 -04001148 "[device {}] Received request from untrusted peer",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001149 req.owner->getLongId());
1150 }
1151 });
1152 }
1153
1154 return true;
1155 },
1156 dht::Value::UserTypeFilter("peer_request"));
1157}
1158
1159void
Adrien Béraud75754b22023-10-17 09:16:06 -04001160ConnectionManager::Impl::onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
1161 const std::shared_ptr<ConnectionInfo>& info,
1162 bool ok,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001163 const DeviceId& deviceId,
1164 const dht::Value::Id& vid,
1165 const std::string& name)
1166{
1167 if (isDestroying_)
1168 return;
1169 // Note: only handle pendingCallbacks here for TLS initied by connectDevice()
1170 // Note: if not initied by connectDevice() the channel name will be empty (because no channel
1171 // asked yet)
1172 auto isDhtRequest = name.empty();
1173 if (!ok) {
1174 if (isDhtRequest) {
1175 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001176 config_->logger->error("[device {}] TLS connection failure - Initied by DHT request. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001177 deviceId,
1178 name,
1179 vid);
1180 if (connReadyCb_)
1181 connReadyCb_(deviceId, "", nullptr);
1182 } else {
1183 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001184 config_->logger->error("[device {}] TLS connection failure - Initied by connectDevice. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001185 deviceId,
1186 name,
1187 vid);
Adrien Béraud75754b22023-10-17 09:16:06 -04001188 dinfo->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001189 }
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001190
Adrien Béraud024c46f2024-03-02 23:53:18 -05001191 std::unique_lock lk(dinfo->mtx_);
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001192 dinfo->info.erase(vid);
1193
1194 if (dinfo->empty()) {
1195 infos_.removeDeviceInfo(dinfo->deviceId);
1196 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001197 } else {
1198 // The socket is ready, store it
1199 if (isDhtRequest) {
1200 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001201 config_->logger->debug("[device {}] Connection is ready - Initied by DHT request. Vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001202 deviceId,
1203 vid);
1204 } else {
1205 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001206 config_->logger->debug("[device {}] Connection is ready - Initied by connectDevice(). channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001207 deviceId,
1208 name,
1209 vid);
1210 }
1211
Adrien Béraud75754b22023-10-17 09:16:06 -04001212 // Note: do not remove pending there it's done in sendChannelRequest
Adrien Béraud024c46f2024-03-02 23:53:18 -05001213 std::unique_lock lk2 {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001214 auto pendingIds = dinfo->requestPendingOps();
Adrien Béraudaca437e2024-09-23 16:40:34 -04001215 auto previousConnections = dinfo->getConnectedInfos();
Adrien Béraud75754b22023-10-17 09:16:06 -04001216 lk2.unlock();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001217 std::unique_lock lk {info->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001218 addNewMultiplexedSocket(dinfo, deviceId, vid, info);
Adrien Béraud75754b22023-10-17 09:16:06 -04001219 lk.unlock();
Adrien Béraudaca437e2024-09-23 16:40:34 -04001220 // send beacon to existing connections for this device
1221 if (config_->logger and not previousConnections.empty())
1222 config_->logger->warn("[device {}] Sending beacon to {} existing connections",
1223 deviceId,
1224 previousConnections.size());
1225 for (const auto& cinfo: previousConnections) {
1226 std::lock_guard lk {cinfo->mutex_};
1227 if (cinfo->socket_) {
1228 cinfo->socket_->sendBeacon();
1229 }
1230 }
1231 // Finally, launch pending callbacks
Adrien Béraud75754b22023-10-17 09:16:06 -04001232 for (const auto& [id, name]: pendingIds) {
1233 if (config_->logger)
1234 config_->logger->debug("[device {}] Send request on TLS socket for channel {}",
1235 deviceId, name);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001236 sendChannelRequest(dinfo, info, info->socket_, name, id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001237 }
1238 }
1239}
1240
1241void
1242ConnectionManager::Impl::answerTo(IceTransport& ice,
1243 const dht::Value::Id& id,
1244 const std::shared_ptr<dht::crypto::PublicKey>& from)
1245{
1246 // NOTE: This is a shortest version of a real SDP message to save some bits
1247 auto iceAttributes = ice.getLocalAttributes();
1248 std::ostringstream icemsg;
1249 icemsg << iceAttributes.ufrag << "\n";
1250 icemsg << iceAttributes.pwd << "\n";
1251 for (const auto& addr : ice.getLocalCandidates(1)) {
1252 icemsg << addr << "\n";
1253 }
1254
1255 // Send PeerConnection response
1256 PeerConnectionRequest val;
1257 val.id = id;
1258 val.ice_msg = icemsg.str();
1259 val.isAnswer = true;
1260 auto value = std::make_shared<dht::Value>(std::move(val));
1261 value->user_type = "peer_request";
1262
1263 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001264 config_->logger->debug("[device {}] Connection accepted, DHT reply", from->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001265 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
1266 + from->getId().toString()),
1267 from,
1268 value,
1269 [from,l=config_->logger](bool ok) {
1270 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -04001271 l->debug("[device {}] Answer to connection request: put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001272 from->getLongId(),
1273 (ok ? "ok" : "failed"));
1274 });
1275}
1276
1277bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001278ConnectionManager::Impl::onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001279{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001280 if (!info)
1281 return false;
1282
Adrien Béraud75754b22023-10-17 09:16:06 -04001283 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001284 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001285 auto& ice = info->ice_;
1286 if (!ice) {
1287 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001288 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001289 if (connReadyCb_)
1290 connReadyCb_(deviceId, "", nullptr);
1291 return false;
1292 }
1293
1294 auto sdp = ice->parseIceCandidates(req.ice_msg);
1295 answerTo(*ice, req.id, req.owner);
1296 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
1297 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001298 config_->logger->error("[device {}] Start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001299 ice = nullptr;
1300 if (connReadyCb_)
1301 connReadyCb_(deviceId, "", nullptr);
1302 return false;
1303 }
1304 return true;
1305}
1306
1307bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001308ConnectionManager::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 -04001309{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001310 if (!info)
1311 return false;
1312
Adrien Béraud75754b22023-10-17 09:16:06 -04001313 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001314 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001315 auto& ice = info->ice_;
1316 if (!ice) {
1317 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001318 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001319 return false;
1320 }
1321
1322 // Build socket
1323 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
1324 std::move(ice)),
1325 false);
1326
1327 // init TLS session
1328 auto ph = req.from;
1329 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001330 config_->logger->debug("[device {}] Start TLS session - Initied by DHT request. vid: {}",
1331 deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001332 req.id);
1333 info->tls_ = std::make_unique<TlsSocketEndpoint>(
1334 std::move(endpoint),
1335 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -04001336 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001337 identity(),
1338 dhParams(),
Adrien Béraud75754b22023-10-17 09:16:06 -04001339 [ph, deviceId, w=weak_from_this(), l=config_->logger](const dht::crypto::Certificate& cert) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001340 auto shared = w.lock();
1341 if (!shared)
1342 return false;
Adrien Béraud9efbd442023-08-27 12:38:07 -04001343 if (cert.getPublicKey().getId() != ph
1344 || deviceId != cert.getPublicKey().getLongId()) {
1345 if (l) l->warn("[device {}] TLS certificate with ID {} doesn't match the DHT request.",
1346 deviceId,
1347 cert.getPublicKey().getLongId());
1348 return false;
1349 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001350 auto crt = shared->certStore().getCertificate(cert.getLongId().toString());
1351 if (!crt)
1352 return false;
1353 return crt->getPacked() == cert.getPacked();
1354 });
1355
1356 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001357 [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 -04001358 if (auto shared = w.lock())
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -05001359 if (auto info = winfo.lock()) {
1360 shared->onTlsNegotiationDone(dinfo.lock(), winfo.lock(), ok, deviceId, vid);
1361 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
1362 dht::ThreadPool::io().run([info = std::move(info)] {});
1363 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001364 });
1365 return true;
1366}
1367
1368void
1369ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
1370 const std::shared_ptr<dht::crypto::Certificate>& /*cert*/)
1371{
1372 auto deviceId = req.owner->getLongId();
1373 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001374 config_->logger->debug("[device {}] New connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001375 if (!iceReqCb_ || !iceReqCb_(deviceId)) {
1376 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001377 config_->logger->debug("[device {}] Refusing connection", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001378 return;
1379 }
1380
1381 // Because the connection is accepted, create an ICE socket.
Adrien Béraud75754b22023-10-17 09:16:06 -04001382 getIceOptions([w = weak_from_this(), req, deviceId](auto&& ice_config) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001383 auto shared = w.lock();
1384 if (!shared)
1385 return;
Adrien Béraud75754b22023-10-17 09:16:06 -04001386
1387 auto di = shared->infos_.createDeviceInfo(deviceId);
1388 auto info = std::make_shared<ConnectionInfo>();
1389 auto wdi = std::weak_ptr(di);
1390 auto winfo = std::weak_ptr(info);
1391
Adrien Béraud612b55b2023-05-29 10:42:04 -04001392 // Note: used when the ice negotiation fails to erase
1393 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -04001394 auto eraseInfo = [w, wdi, id = req.id] {
1395 auto shared = w.lock();
1396 if (auto di = wdi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001397 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001398 di->info.erase(id);
1399 auto ops = di->extractPendingOperations(id, nullptr);
1400 if (di->empty()) {
1401 if (shared)
1402 shared->infos_.removeDeviceInfo(di->deviceId);
1403 }
1404 lk.unlock();
1405 for (const auto& op: ops)
1406 op.cb(nullptr, di->deviceId);
1407 if (shared && shared->connReadyCb_)
1408 shared->connReadyCb_(di->deviceId, "", nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001409 }
1410 };
1411
Adrien Béraud75754b22023-10-17 09:16:06 -04001412 ice_config.master = true;
1413 ice_config.streamsCount = 1;
1414 ice_config.compCountPerStream = 1; // TCP
Adrien Béraud612b55b2023-05-29 10:42:04 -04001415 ice_config.tcpEnable = true;
Adrien Béraud75754b22023-10-17 09:16:06 -04001416 ice_config.onInitDone = [w, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001417 auto shared = w.lock();
1418 if (!shared)
1419 return;
1420 if (!ok) {
1421 if (shared->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001422 shared->config_->logger->error("[device {}] Unable to initialize ICE session.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001423 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1424 return;
1425 }
1426
1427 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001428 [w = std::move(w), winfo = std::move(winfo), req = std::move(req), eraseInfo = std::move(eraseInfo)] {
1429 if (auto shared = w.lock()) {
1430 if (!shared->onRequestStartIce(winfo.lock(), req))
1431 eraseInfo();
1432 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001433 });
1434 };
1435
Adrien Béraud75754b22023-10-17 09:16:06 -04001436 ice_config.onNegoDone = [w, wdi, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001437 auto shared = w.lock();
1438 if (!shared)
1439 return;
1440 if (!ok) {
1441 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001442 shared->config_->logger->error("[device {}] ICE negotiation failed.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001443 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1444 return;
1445 }
1446
1447 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001448 [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 -04001449 if (auto shared = w.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001450 if (!shared->onRequestOnNegoDone(wdi.lock(), winfo.lock(), req))
Adrien Béraud612b55b2023-05-29 10:42:04 -04001451 eraseInfo();
1452 });
1453 };
1454
1455 // Negotiate a new ICE socket
Adrien Béraud612b55b2023-05-29 10:42:04 -04001456 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001457 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001458 di->info[req.id] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001459 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001460
Adrien Béraud612b55b2023-05-29 10:42:04 -04001461 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001462 shared->config_->logger->debug("[device {}] Accepting connection", deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001463 std::unique_lock lk {info->mutex_};
Sébastien Blin34086512023-07-25 09:52:14 -04001464 info->ice_ = shared->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001465 if (not info->ice_) {
1466 if (shared->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001467 shared->config_->logger->error("[device {}] Unable to initialize ICE session", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001468 eraseInfo();
1469 return;
1470 }
1471 // We need to detect any shutdown if the ice session is destroyed before going to the TLS session;
1472 info->ice_->setOnShutdown([eraseInfo]() {
1473 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1474 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001475 try {
1476 info->ice_->initIceInstance(ice_config);
1477 } catch (const std::exception& e) {
1478 if (shared->config_->logger)
1479 shared->config_->logger->error("{}", e.what());
1480 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1481 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001482 });
1483}
1484
1485void
Adrien Béraud75754b22023-10-17 09:16:06 -04001486ConnectionManager::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 -04001487{
Adrien Béraud75754b22023-10-17 09:16:06 -04001488 info->socket_ = std::make_shared<MultiplexedSocket>(config_->ioContext, deviceId, std::move(info->tls_), config_->logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001489 info->socket_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001490 [w = weak_from_this()](const DeviceId& deviceId, const std::shared_ptr<ChannelSocket>& socket) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001491 if (auto sthis = w.lock())
1492 if (sthis->connReadyCb_)
1493 sthis->connReadyCb_(deviceId, socket->name(), socket);
1494 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001495 info->socket_->setOnRequest([w = weak_from_this()](const std::shared_ptr<dht::crypto::Certificate>& peer,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001496 const uint16_t&,
1497 const std::string& name) {
1498 if (auto sthis = w.lock())
1499 if (sthis->channelReqCb_)
1500 return sthis->channelReqCb_(peer, name);
1501 return false;
1502 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001503 info->socket_->onShutdown([dinfo, wi=std::weak_ptr(info), vid]() {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001504 // Cancel current outgoing connections
Adrien Béraud75754b22023-10-17 09:16:06 -04001505 dht::ThreadPool::io().run([dinfo, wi, vid] {
1506 std::set<dht::Value::Id> ids;
1507 if (auto info = wi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001508 std::lock_guard lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001509 if (info->socket_) {
1510 ids = std::move(info->cbIds_);
1511 info->socket_->shutdown();
1512 }
1513 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001514 if (auto deviceInfo = dinfo.lock()) {
1515 std::shared_ptr<ConnectionInfo> info;
1516 std::vector<PendingCb> ops;
Adrien Béraud024c46f2024-03-02 23:53:18 -05001517 std::unique_lock lk(deviceInfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001518 auto it = deviceInfo->info.find(vid);
1519 if (it != deviceInfo->info.end()) {
1520 info = std::move(it->second);
1521 deviceInfo->info.erase(it);
1522 }
1523 for (const auto& cbId : ids) {
1524 auto po = deviceInfo->extractPendingOperations(cbId, nullptr);
1525 ops.insert(ops.end(), po.begin(), po.end());
1526 }
1527 lk.unlock();
1528 for (auto& op : ops)
1529 op.cb(nullptr, deviceInfo->deviceId);
1530 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001531 });
1532 });
1533}
1534
1535const std::shared_future<tls::DhParams>
1536ConnectionManager::Impl::dhParams() const
1537{
1538 return dht::ThreadPool::computation().get<tls::DhParams>(
Adrien Béraud2a4e73d2023-08-27 12:53:55 -04001539 std::bind(tls::DhParams::loadDhParams, config_->cachePath / "dhParams"));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001540}
1541
Adrien Béraud612b55b2023-05-29 10:42:04 -04001542bool
Adrien Béraud5aec4102024-02-22 14:15:56 -05001543ConnectionManager::Impl::isMessageTreated(dht::Value::Id id)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001544{
Adrien Béraud024c46f2024-03-02 23:53:18 -05001545 std::lock_guard lock(messageMutex_);
Adrien Béraud28e2ca52024-02-23 14:19:59 -05001546 return !treatedMessages_.add(id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001547}
1548
Adrien Béraud612b55b2023-05-29 10:42:04 -04001549
1550IpAddr
1551ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
1552{
1553 if (family == AF_INET)
1554 return publishedIp_[0];
1555 if (family == AF_INET6)
1556 return publishedIp_[1];
1557
1558 assert(family == AF_UNSPEC);
1559
1560 // If family is not set, prefere IPv4 if available. It's more
1561 // likely to succeed behind NAT.
1562 if (publishedIp_[0])
1563 return publishedIp_[0];
1564 if (publishedIp_[1])
1565 return publishedIp_[1];
1566 return {};
1567}
1568
1569void
1570ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr)
1571{
1572 if (ip_addr.getFamily() == AF_INET) {
1573 publishedIp_[0] = ip_addr;
1574 } else {
1575 publishedIp_[1] = ip_addr;
1576 }
1577}
1578
1579void
1580ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb)
1581{
Adrien Béraud75754b22023-10-17 09:16:06 -04001582 dht()->getPublicAddress([w=weak_from_this(), cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
Sébastien Blinb6504372023-10-12 10:35:35 -04001583 auto shared = w.lock();
1584 if (!shared)
1585 return;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001586 bool hasIpv4 {false}, hasIpv6 {false};
1587 for (auto& result : results) {
1588 auto family = result.getFamily();
1589 if (family == AF_INET) {
1590 if (not hasIpv4) {
1591 hasIpv4 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001592 if (shared->config_->logger)
1593 shared->config_->logger->debug("Store DHT public IPv4 address: {}", result);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001594 //JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str());
Sébastien Blinb6504372023-10-12 10:35:35 -04001595 shared->setPublishedAddress(*result.get());
1596 if (shared->config_->upnpCtrl) {
1597 shared->config_->upnpCtrl->setPublicAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001598 }
1599 }
1600 } else if (family == AF_INET6) {
1601 if (not hasIpv6) {
1602 hasIpv6 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001603 if (shared->config_->logger)
1604 shared->config_->logger->debug("Store DHT public IPv6 address: {}", result);
1605 shared->setPublishedAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001606 }
1607 }
1608 if (hasIpv4 and hasIpv6)
1609 break;
1610 }
1611 if (cb)
1612 cb();
1613 });
1614}
1615
1616void
1617ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1618{
1619 storeActiveIpAddress([this, cb = std::move(cb)] {
1620 IceTransportOptions opts = ConnectionManager::Impl::getIceOptions();
1621 auto publishedAddr = getPublishedIpAddress();
1622
1623 if (publishedAddr) {
1624 auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
1625 publishedAddr.getFamily());
1626 if (interfaceAddr) {
1627 opts.accountLocalAddr = interfaceAddr;
1628 opts.accountPublicAddr = publishedAddr;
1629 }
1630 }
1631 if (cb)
1632 cb(std::move(opts));
1633 });
1634}
1635
1636IceTransportOptions
1637ConnectionManager::Impl::getIceOptions() const noexcept
1638{
1639 IceTransportOptions opts;
Sébastien Blin34086512023-07-25 09:52:14 -04001640 opts.factory = config_->factory;
Amna49da80f2024-08-26 16:54:40 -04001641 opts.upnpEnable = config_->upnpEnabled;
Adrien Béraud7b869d92023-08-21 09:02:35 -04001642 opts.upnpContext = config_->upnpCtrl ? config_->upnpCtrl->upnpContext() : nullptr;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001643
1644 if (config_->stunEnabled)
1645 opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer));
1646 if (config_->turnEnabled) {
Sébastien Blin84bf4182023-07-21 14:18:39 -04001647 if (config_->turnCache) {
1648 auto turnAddr = config_->turnCache->getResolvedTurn();
1649 if (turnAddr != std::nullopt) {
1650 opts.turnServers.emplace_back(TurnServerInfo()
1651 .setUri(turnAddr->toString())
1652 .setUsername(config_->turnServerUserName)
1653 .setPassword(config_->turnServerPwd)
1654 .setRealm(config_->turnServerRealm));
1655 }
1656 } else {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001657 opts.turnServers.emplace_back(TurnServerInfo()
Sébastien Blin84bf4182023-07-21 14:18:39 -04001658 .setUri(config_->turnServer)
1659 .setUsername(config_->turnServerUserName)
1660 .setPassword(config_->turnServerPwd)
1661 .setRealm(config_->turnServerRealm));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001662 }
1663 // NOTE: first test with ipv6 turn was not concluant and resulted in multiple
1664 // co issues. So this needs some debug. for now just disable
1665 // if (cacheTurnV6 && *cacheTurnV6) {
1666 // opts.turnServers.emplace_back(TurnServerInfo()
1667 // .setUri(cacheTurnV6->toString(true))
1668 // .setUsername(turnServerUserName_)
1669 // .setPassword(turnServerPwd_)
1670 // .setRealm(turnServerRealm_));
1671 //}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001672 }
1673 return opts;
1674}
1675
1676bool
1677ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
1678 dht::InfoHash& account_id,
1679 const std::shared_ptr<Logger>& logger)
1680{
1681 if (not crt)
1682 return false;
1683
1684 auto top_issuer = crt;
1685 while (top_issuer->issuer)
1686 top_issuer = top_issuer->issuer;
1687
ovari123a15c6882024-09-17 18:34:20 -04001688 // Unable to self-signed device certificate
Adrien Béraudc631a832023-07-26 22:19:00 -04001689 if (top_issuer == crt) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001690 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001691 logger->warn("Found invalid (self-signed) peer device: {}", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001692 return false;
Adrien Béraudc631a832023-07-26 22:19:00 -04001693 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001694
1695 // Check peer certificate chain
1696 // Trust store with top issuer as the only CA
1697 dht::crypto::TrustList peer_trust;
1698 peer_trust.add(*top_issuer);
1699 if (not peer_trust.verify(*crt)) {
1700 if (logger)
1701 logger->warn("Found invalid peer device: {}", crt->getLongId());
1702 return false;
1703 }
1704
1705 // Check cached OCSP response
1706 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
1707 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001708 logger->error("Certificate {} is disabled by cached OCSP response", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001709 return false;
1710 }
1711
Adrien Béraudc631a832023-07-26 22:19:00 -04001712 account_id = crt->issuer->getId();
1713 if (logger)
1714 logger->warn("Found peer device: {} account:{} CA:{}",
1715 crt->getLongId(),
1716 account_id,
1717 top_issuer->getId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001718 return true;
1719}
1720
1721bool
1722ConnectionManager::Impl::findCertificate(
1723 const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1724{
1725 if (auto cert = certStore().getCertificate(id.toString())) {
1726 if (cb)
1727 cb(cert);
1728 } else if (cb)
1729 cb(nullptr);
1730 return true;
1731}
1732
Sébastien Blin34086512023-07-25 09:52:14 -04001733bool
1734ConnectionManager::Impl::findCertificate(const dht::InfoHash& h,
1735 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1736{
1737 if (auto cert = certStore().getCertificate(h.toString())) {
1738 if (cb)
1739 cb(cert);
1740 } else {
1741 dht()->findCertificate(h,
1742 [cb = std::move(cb), this](
1743 const std::shared_ptr<dht::crypto::Certificate>& crt) {
1744 if (crt)
1745 certStore().pinCertificate(crt);
1746 if (cb)
1747 cb(crt);
1748 });
1749 }
1750 return true;
1751}
1752
Amna81221ad2023-09-14 17:33:26 -04001753std::shared_ptr<ConnectionManager::Config>
1754buildDefaultConfig(dht::crypto::Identity id){
1755 auto conf = std::make_shared<ConnectionManager::Config>();
1756 conf->id = std::move(id);
1757 return conf;
1758}
1759
Adrien Béraud612b55b2023-05-29 10:42:04 -04001760ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_)
1761 : pimpl_ {std::make_shared<Impl>(config_)}
1762{}
1763
Amna81221ad2023-09-14 17:33:26 -04001764ConnectionManager::ConnectionManager(dht::crypto::Identity id)
1765 : ConnectionManager {buildDefaultConfig(id)}
1766{}
1767
Adrien Béraud612b55b2023-05-29 10:42:04 -04001768ConnectionManager::~ConnectionManager()
1769{
1770 if (pimpl_)
1771 pimpl_->shutdown();
1772}
1773
1774void
1775ConnectionManager::connectDevice(const DeviceId& deviceId,
1776 const std::string& name,
1777 ConnectCallback cb,
1778 bool noNewSocket,
1779 bool forceNewSocket,
1780 const std::string& connType)
1781{
1782 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1783}
1784
1785void
Amna0cf544d2023-07-25 14:25:09 -04001786ConnectionManager::connectDevice(const dht::InfoHash& deviceId,
1787 const std::string& name,
1788 ConnectCallbackLegacy cb,
1789 bool noNewSocket,
1790 bool forceNewSocket,
1791 const std::string& connType)
1792{
1793 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1794}
1795
1796
1797void
Adrien Béraud612b55b2023-05-29 10:42:04 -04001798ConnectionManager::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
1799 const std::string& name,
1800 ConnectCallback cb,
1801 bool noNewSocket,
1802 bool forceNewSocket,
1803 const std::string& connType)
1804{
1805 pimpl_->connectDevice(cert, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1806}
1807
1808bool
1809ConnectionManager::isConnecting(const DeviceId& deviceId, const std::string& name) const
1810{
Adrien Béraud75754b22023-10-17 09:16:06 -04001811 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001812 std::unique_lock lk {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001813 return dinfo->isConnecting(name);
Adrien Béraud75754b22023-10-17 09:16:06 -04001814 }
1815 return false;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001816}
1817
Sébastien Blind0c92c72023-12-07 15:27:51 -05001818bool
1819ConnectionManager::isConnected(const DeviceId& deviceId) const
1820{
1821 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001822 std::unique_lock lk {dinfo->mtx_};
Sébastien Blind0c92c72023-12-07 15:27:51 -05001823 return dinfo->getConnectedInfo() != nullptr;
1824 }
1825 return false;
1826}
1827
Adrien Béraud612b55b2023-05-29 10:42:04 -04001828void
1829ConnectionManager::closeConnectionsWith(const std::string& peerUri)
1830{
Adrien Béraud75754b22023-10-17 09:16:06 -04001831 std::vector<std::shared_ptr<DeviceInfo>> dInfos;
1832 for (const auto& dinfo: pimpl_->infos_.getDeviceInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001833 std::unique_lock lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001834 bool isPeer = false;
1835 for (auto const& [id, cinfo]: dinfo->info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001836 std::lock_guard lkv {cinfo->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001837 auto tls = cinfo->tls_ ? cinfo->tls_.get() : (cinfo->socket_ ? cinfo->socket_->endpoint() : nullptr);
Adrien Béraudafa8e282023-09-24 12:53:20 -04001838 auto cert = tls ? tls->peerCertificate() : nullptr;
1839 if (not cert)
Adrien Béraud75754b22023-10-17 09:16:06 -04001840 cert = pimpl_->certStore().getCertificate(dinfo->deviceId.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001841 if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001842 isPeer = true;
1843 break;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001844 }
1845 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001846 lk.unlock();
1847 if (isPeer) {
1848 dInfos.emplace_back(std::move(dinfo));
1849 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001850 }
1851 // Stop connections to all peers devices
Adrien Béraud75754b22023-10-17 09:16:06 -04001852 for (const auto& dinfo : dInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001853 std::unique_lock lk {dinfo->mtx_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001854 auto unused = dinfo->extractUnusedConnections();
1855 auto pending = dinfo->extractPendingOperations(0, nullptr);
1856 pimpl_->infos_.removeDeviceInfo(dinfo->deviceId);
1857 lk.unlock();
1858 for (auto& op : unused)
1859 op->shutdown();
1860 for (auto& op : pending)
1861 op.cb(nullptr, dinfo->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001862 }
1863}
1864
1865void
1866ConnectionManager::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1867{
1868 pimpl_->onDhtConnected(devicePk);
1869}
1870
1871void
1872ConnectionManager::onICERequest(onICERequestCallback&& cb)
1873{
1874 pimpl_->iceReqCb_ = std::move(cb);
1875}
1876
1877void
1878ConnectionManager::onChannelRequest(ChannelRequestCallback&& cb)
1879{
1880 pimpl_->channelReqCb_ = std::move(cb);
1881}
1882
1883void
1884ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb)
1885{
1886 pimpl_->connReadyCb_ = std::move(cb);
1887}
1888
1889void
1890ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb)
1891{
1892 pimpl_->iOSConnectedCb_ = std::move(cb);
1893}
1894
1895std::size_t
1896ConnectionManager::activeSockets() const
1897{
Adrien Béraud75754b22023-10-17 09:16:06 -04001898 return pimpl_->infos_.getConnectedInfos().size();
Adrien Béraud612b55b2023-05-29 10:42:04 -04001899}
1900
1901void
1902ConnectionManager::monitor() const
1903{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001904 auto logger = pimpl_->config_->logger;
1905 if (!logger)
1906 return;
1907 logger->debug("ConnectionManager current status:");
Adrien Béraud75754b22023-10-17 09:16:06 -04001908 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001909 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001910 if (ci->socket_)
1911 ci->socket_->monitor();
1912 }
1913 logger->debug("ConnectionManager end status.");
1914}
1915
1916void
1917ConnectionManager::connectivityChanged()
1918{
Adrien Béraud75754b22023-10-17 09:16:06 -04001919 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001920 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001921 if (ci->socket_)
Adrien Béraud51a54712023-10-17 21:24:30 -04001922 dht::ThreadPool::io().run([s = ci->socket_] { s->sendBeacon(); });
Adrien Béraud612b55b2023-05-29 10:42:04 -04001923 }
1924}
1925
1926void
1927ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1928{
1929 return pimpl_->getIceOptions(std::move(cb));
1930}
1931
1932IceTransportOptions
1933ConnectionManager::getIceOptions() const noexcept
1934{
1935 return pimpl_->getIceOptions();
1936}
1937
1938IpAddr
1939ConnectionManager::getPublishedIpAddress(uint16_t family) const
1940{
1941 return pimpl_->getPublishedIpAddress(family);
1942}
1943
1944void
1945ConnectionManager::setPublishedAddress(const IpAddr& ip_addr)
1946{
1947 return pimpl_->setPublishedAddress(ip_addr);
1948}
1949
1950void
1951ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb)
1952{
1953 return pimpl_->storeActiveIpAddress(std::move(cb));
1954}
1955
1956std::shared_ptr<ConnectionManager::Config>
1957ConnectionManager::getConfig()
1958{
1959 return pimpl_->config_;
1960}
1961
Amna31791e52023-08-03 12:40:57 -04001962std::vector<std::map<std::string, std::string>>
1963ConnectionManager::getConnectionList(const DeviceId& device) const
1964{
1965 std::vector<std::map<std::string, std::string>> connectionsList;
Amna31791e52023-08-03 12:40:57 -04001966 if (device) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001967 if (auto deviceInfo = pimpl_->infos_.getDeviceInfo(device)) {
1968 connectionsList = deviceInfo->getConnectionList(pimpl_->certStore());
Amna31791e52023-08-03 12:40:57 -04001969 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001970 } else {
1971 for (const auto& deviceInfo : pimpl_->infos_.getDeviceInfos()) {
1972 auto cl = deviceInfo->getConnectionList(pimpl_->certStore());
1973 connectionsList.insert(connectionsList.end(), std::make_move_iterator(cl.begin()), std::make_move_iterator(cl.end()));
Amna31791e52023-08-03 12:40:57 -04001974 }
1975 }
1976 return connectionsList;
1977}
1978
1979std::vector<std::map<std::string, std::string>>
1980ConnectionManager::getChannelList(const std::string& connectionId) const
1981{
Adrien Béraud75754b22023-10-17 09:16:06 -04001982 auto [deviceId, valueId] = parseCallbackId(connectionId);
1983 if (auto info = pimpl_->infos_.getInfo(deviceId, valueId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001984 std::lock_guard lk(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001985 if (info->socket_)
1986 return info->socket_->getChannelList();
Amna31791e52023-08-03 12:40:57 -04001987 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001988 return {};
Amna31791e52023-08-03 12:40:57 -04001989}
1990
Sébastien Blin464bdff2023-07-19 08:02:53 -04001991} // namespace dhtnet