blob: 138afc5fbd98f34f68f143513d24280523a5e7de [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_) {
886 info->cbIds_.emplace(vid);
Adrien Béraudb941e922023-10-16 12:56:14 -0400887 diw.requested = true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400888 lkc.unlock();
889 lk.unlock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400890 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400891 sthis->config_->logger->debug("[device {}] Peer already connected. Add a new channel", deviceId);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400892 sthis->sendChannelRequest(di, info, sock, name, vid);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400893 return;
894 }
895 }
896
897 if (isConnectingToDevice && !forceNewSocket) {
898 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400899 sthis->config_->logger->debug("[device {}] Already connecting, wait for ICE negotiation", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400900 return;
901 }
902 if (noNewSocket) {
903 // If no new socket is specified, we don't try to generate a new socket
Adrien Béraud75754b22023-10-17 09:16:06 -0400904 di->executePendingOperations(lk, vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400905 return;
906 }
907
908 // Note: used when the ice negotiation fails to erase
909 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -0400910 auto eraseInfo = [w, diw=std::weak_ptr(di), vid] {
911 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500912 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400913 di->info.erase(vid);
914 auto ops = di->extractPendingOperations(vid, nullptr);
915 if (di->empty()) {
916 if (auto shared = w.lock())
917 shared->infos_.removeDeviceInfo(di->deviceId);
918 }
919 lk.unlock();
920 for (const auto& op: ops)
921 op.cb(nullptr, di->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400922 }
923 };
924
925 // If no socket exists, we need to initiate an ICE connection.
926 sthis->getIceOptions([w,
927 deviceId = std::move(deviceId),
928 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400929 diw=std::weak_ptr(di),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400930 name = std::move(name),
931 cert = std::move(cert),
932 vid,
933 connType,
934 eraseInfo](auto&& ice_config) {
935 auto sthis = w.lock();
936 if (!sthis) {
937 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
938 return;
939 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400940 auto info = std::make_shared<ConnectionInfo>();
941 auto winfo = std::weak_ptr(info);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400942 ice_config.tcpEnable = true;
943 ice_config.onInitDone = [w,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400944 devicePk = std::move(devicePk),
945 name = std::move(name),
946 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400947 diw,
948 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400949 vid,
950 connType,
951 eraseInfo](bool ok) {
952 dht::ThreadPool::io().run([w = std::move(w),
953 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400954 vid,
955 winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400956 eraseInfo,
957 connType, ok] {
958 auto sthis = w.lock();
959 if (!ok && sthis && sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -0400960 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", devicePk->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400961 if (!sthis || !ok) {
962 eraseInfo();
963 return;
964 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400965 sthis->connectDeviceStartIce(winfo.lock(), devicePk, vid, connType, [=](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400966 if (!ok) {
967 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
968 }
969 });
970 });
971 };
972 ice_config.onNegoDone = [w,
973 deviceId,
974 name,
975 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400976 diw,
977 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400978 vid,
979 eraseInfo](bool ok) {
980 dht::ThreadPool::io().run([w = std::move(w),
981 deviceId = std::move(deviceId),
982 name = std::move(name),
983 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400984 diw = std::move(diw),
985 winfo = std::move(winfo),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400986 vid = std::move(vid),
987 eraseInfo = std::move(eraseInfo),
988 ok] {
989 auto sthis = w.lock();
990 if (!ok && sthis && sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400991 sthis->config_->logger->error("[device {}] ICE negotiation failed.", deviceId);
Adrien Béraud75754b22023-10-17 09:16:06 -0400992 if (!sthis || !ok || !sthis->connectDeviceOnNegoDone(diw, winfo.lock(), deviceId, name, vid, cert))
Adrien Béraud612b55b2023-05-29 10:42:04 -0400993 eraseInfo();
994 });
995 };
996
Adrien Béraud75754b22023-10-17 09:16:06 -0400997 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500998 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400999 di->info[vid] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001000 }
Adrien Béraud024c46f2024-03-02 23:53:18 -05001001 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001002 ice_config.master = false;
1003 ice_config.streamsCount = 1;
1004 ice_config.compCountPerStream = 1;
Sébastien Blin34086512023-07-25 09:52:14 -04001005 info->ice_ = sthis->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001006 if (!info->ice_) {
1007 if (sthis->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001008 sthis->config_->logger->error("[device {}] Unable to initialize ICE session.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001009 eraseInfo();
1010 return;
1011 }
1012 // We need to detect any shutdown if the ice session is destroyed before going to the
1013 // TLS session;
1014 info->ice_->setOnShutdown([eraseInfo]() {
1015 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1016 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001017 try {
1018 info->ice_->initIceInstance(ice_config);
1019 } catch (const std::exception& e) {
1020 if (sthis->config_->logger)
1021 sthis->config_->logger->error("{}", e.what());
1022 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1023 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001024 });
1025 });
1026}
1027
1028void
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001029ConnectionManager::Impl::sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfow,
1030 const std::weak_ptr<ConnectionInfo>& cinfow,
Adrien Béraud75754b22023-10-17 09:16:06 -04001031 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001032 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001033 const dht::Value::Id& vid)
1034{
1035 auto channelSock = sock->addChannel(name);
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001036 if (!channelSock) {
1037 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001038 config_->logger->error("sendChannelRequest failed - unable to create channel");
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001039 if (auto info = dinfow.lock())
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001040 info->executePendingOperations(vid, nullptr);
1041 return;
1042 }
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001043 channelSock->onShutdown([dinfow, name, vid] {
1044 if (auto info = dinfow.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001045 info->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001046 });
1047 channelSock->onReady(
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001048 [dinfow, cinfow, wSock = std::weak_ptr(channelSock), name, vid](bool accepted) {
1049 if (auto dinfo = dinfow.lock()) {
1050 dinfo->executePendingOperations(vid, accepted ? wSock.lock() : nullptr, accepted);
Sébastien Blinad161572024-01-31 14:14:51 -05001051 // Always lock top-down cinfo->mutex
1052 dht::ThreadPool::io().run([cinfow, vid]() {
1053 if (auto cinfo = cinfow.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001054 std::lock_guard lk(cinfo->mutex_);
Sébastien Blinad161572024-01-31 14:14:51 -05001055 cinfo->cbIds_.erase(vid);
1056 }
1057 });
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001058 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001059 });
1060
1061 ChannelRequest val;
1062 val.name = channelSock->name();
1063 val.state = ChannelRequestState::REQUEST;
1064 val.channel = channelSock->channel();
1065 msgpack::sbuffer buffer(256);
1066 msgpack::pack(buffer, val);
1067
1068 std::error_code ec;
1069 int res = sock->write(CONTROL_CHANNEL,
1070 reinterpret_cast<const uint8_t*>(buffer.data()),
1071 buffer.size(),
1072 ec);
1073 if (res < 0) {
1074 // TODO check if we should handle errors here
1075 if (config_->logger)
Adrien Béraud75754b22023-10-17 09:16:06 -04001076 config_->logger->error("sendChannelRequest failed - error: {}", ec.message());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001077 }
1078}
1079
1080void
Adrien Béraud1addf952023-09-30 17:38:35 -04001081ConnectionManager::Impl::onPeerResponse(PeerConnectionRequest&& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001082{
1083 auto device = req.owner->getLongId();
Adrien Béraud75754b22023-10-17 09:16:06 -04001084 if (auto info = infos_.getInfo(device, req.id)) {
Adrien Béraud23852462023-07-22 01:46:27 -04001085 if (config_->logger)
1086 config_->logger->debug("[device {}] New response received", device);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001087 std::lock_guard lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001088 info->responseReceived_ = true;
1089 info->response_ = std::move(req);
1090 info->waitForAnswer_->expires_at(std::chrono::steady_clock::now());
1091 info->waitForAnswer_->async_wait(std::bind(&ConnectionManager::Impl::onResponse,
1092 this,
1093 std::placeholders::_1,
Adrien Béraud75754b22023-10-17 09:16:06 -04001094 std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -04001095 device,
1096 req.id));
1097 } else {
1098 if (config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001099 config_->logger->warn("[device {}] Response received, but unable to find request", device);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001100 }
1101}
1102
1103void
1104ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1105{
1106 if (!dht())
1107 return;
1108 dht()->listen<PeerConnectionRequest>(
1109 dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()),
Adrien Béraud75754b22023-10-17 09:16:06 -04001110 [w = weak_from_this()](PeerConnectionRequest&& req) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001111 auto shared = w.lock();
1112 if (!shared)
1113 return false;
Adrien Béraud5aec4102024-02-22 14:15:56 -05001114 if (shared->isMessageTreated(req.id)) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001115 // Message already treated. Just ignore
1116 return true;
1117 }
1118 if (req.isAnswer) {
1119 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001120 shared->config_->logger->debug("[device {}] Received request answer", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001121 } else {
1122 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001123 shared->config_->logger->debug("[device {}] Received request", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001124 }
1125 if (req.isAnswer) {
Adrien Béraud1addf952023-09-30 17:38:35 -04001126 shared->onPeerResponse(std::move(req));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001127 } else {
1128 // Async certificate checking
Sébastien Blin34086512023-07-25 09:52:14 -04001129 shared->findCertificate(
Adrien Béraud612b55b2023-05-29 10:42:04 -04001130 req.from,
1131 [w, req = std::move(req)](
1132 const std::shared_ptr<dht::crypto::Certificate>& cert) mutable {
1133 auto shared = w.lock();
1134 if (!shared)
1135 return;
1136 dht::InfoHash peer_h;
1137 if (foundPeerDevice(cert, peer_h, shared->config_->logger)) {
1138#if TARGET_OS_IOS
1139 if (shared->iOSConnectedCb_(req.connType, peer_h))
1140 return;
1141#endif
1142 shared->onDhtPeerRequest(req, cert);
1143 } else {
1144 if (shared->config_->logger)
1145 shared->config_->logger->warn(
Adrien Béraud23852462023-07-22 01:46:27 -04001146 "[device {}] Received request from untrusted peer",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001147 req.owner->getLongId());
1148 }
1149 });
1150 }
1151
1152 return true;
1153 },
1154 dht::Value::UserTypeFilter("peer_request"));
1155}
1156
1157void
Adrien Béraud75754b22023-10-17 09:16:06 -04001158ConnectionManager::Impl::onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
1159 const std::shared_ptr<ConnectionInfo>& info,
1160 bool ok,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001161 const DeviceId& deviceId,
1162 const dht::Value::Id& vid,
1163 const std::string& name)
1164{
1165 if (isDestroying_)
1166 return;
1167 // Note: only handle pendingCallbacks here for TLS initied by connectDevice()
1168 // Note: if not initied by connectDevice() the channel name will be empty (because no channel
1169 // asked yet)
1170 auto isDhtRequest = name.empty();
1171 if (!ok) {
1172 if (isDhtRequest) {
1173 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001174 config_->logger->error("[device {}] TLS connection failure - Initied by DHT request. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001175 deviceId,
1176 name,
1177 vid);
1178 if (connReadyCb_)
1179 connReadyCb_(deviceId, "", nullptr);
1180 } else {
1181 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001182 config_->logger->error("[device {}] TLS connection failure - Initied by connectDevice. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001183 deviceId,
1184 name,
1185 vid);
Adrien Béraud75754b22023-10-17 09:16:06 -04001186 dinfo->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001187 }
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001188
Adrien Béraud024c46f2024-03-02 23:53:18 -05001189 std::unique_lock lk(dinfo->mtx_);
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001190 dinfo->info.erase(vid);
1191
1192 if (dinfo->empty()) {
1193 infos_.removeDeviceInfo(dinfo->deviceId);
1194 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001195 } else {
1196 // The socket is ready, store it
1197 if (isDhtRequest) {
1198 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001199 config_->logger->debug("[device {}] Connection is ready - Initied by DHT request. Vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001200 deviceId,
1201 vid);
1202 } else {
1203 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001204 config_->logger->debug("[device {}] Connection is ready - Initied by connectDevice(). channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001205 deviceId,
1206 name,
1207 vid);
1208 }
1209
Adrien Béraud75754b22023-10-17 09:16:06 -04001210 // Note: do not remove pending there it's done in sendChannelRequest
Adrien Béraud024c46f2024-03-02 23:53:18 -05001211 std::unique_lock lk2 {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001212 auto pendingIds = dinfo->requestPendingOps();
Adrien Béraudaca437e2024-09-23 16:40:34 -04001213 auto previousConnections = dinfo->getConnectedInfos();
Adrien Béraud75754b22023-10-17 09:16:06 -04001214 lk2.unlock();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001215 std::unique_lock lk {info->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001216 addNewMultiplexedSocket(dinfo, deviceId, vid, info);
Adrien Béraud75754b22023-10-17 09:16:06 -04001217 lk.unlock();
Adrien Béraudaca437e2024-09-23 16:40:34 -04001218 // send beacon to existing connections for this device
1219 if (config_->logger and not previousConnections.empty())
1220 config_->logger->warn("[device {}] Sending beacon to {} existing connections",
1221 deviceId,
1222 previousConnections.size());
1223 for (const auto& cinfo: previousConnections) {
1224 std::lock_guard lk {cinfo->mutex_};
1225 if (cinfo->socket_) {
1226 cinfo->socket_->sendBeacon();
1227 }
1228 }
1229 // Finally, launch pending callbacks
Adrien Béraud75754b22023-10-17 09:16:06 -04001230 for (const auto& [id, name]: pendingIds) {
1231 if (config_->logger)
1232 config_->logger->debug("[device {}] Send request on TLS socket for channel {}",
1233 deviceId, name);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001234 sendChannelRequest(dinfo, info, info->socket_, name, id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001235 }
1236 }
1237}
1238
1239void
1240ConnectionManager::Impl::answerTo(IceTransport& ice,
1241 const dht::Value::Id& id,
1242 const std::shared_ptr<dht::crypto::PublicKey>& from)
1243{
1244 // NOTE: This is a shortest version of a real SDP message to save some bits
1245 auto iceAttributes = ice.getLocalAttributes();
1246 std::ostringstream icemsg;
1247 icemsg << iceAttributes.ufrag << "\n";
1248 icemsg << iceAttributes.pwd << "\n";
1249 for (const auto& addr : ice.getLocalCandidates(1)) {
1250 icemsg << addr << "\n";
1251 }
1252
1253 // Send PeerConnection response
1254 PeerConnectionRequest val;
1255 val.id = id;
1256 val.ice_msg = icemsg.str();
1257 val.isAnswer = true;
1258 auto value = std::make_shared<dht::Value>(std::move(val));
1259 value->user_type = "peer_request";
1260
1261 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001262 config_->logger->debug("[device {}] Connection accepted, DHT reply", from->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001263 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
1264 + from->getId().toString()),
1265 from,
1266 value,
1267 [from,l=config_->logger](bool ok) {
1268 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -04001269 l->debug("[device {}] Answer to connection request: put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001270 from->getLongId(),
1271 (ok ? "ok" : "failed"));
1272 });
1273}
1274
1275bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001276ConnectionManager::Impl::onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001277{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001278 if (!info)
1279 return false;
1280
Adrien Béraud75754b22023-10-17 09:16:06 -04001281 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001282 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001283 auto& ice = info->ice_;
1284 if (!ice) {
1285 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001286 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001287 if (connReadyCb_)
1288 connReadyCb_(deviceId, "", nullptr);
1289 return false;
1290 }
1291
1292 auto sdp = ice->parseIceCandidates(req.ice_msg);
1293 answerTo(*ice, req.id, req.owner);
1294 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
1295 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001296 config_->logger->error("[device {}] Start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001297 ice = nullptr;
1298 if (connReadyCb_)
1299 connReadyCb_(deviceId, "", nullptr);
1300 return false;
1301 }
1302 return true;
1303}
1304
1305bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001306ConnectionManager::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 -04001307{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001308 if (!info)
1309 return false;
1310
Adrien Béraud75754b22023-10-17 09:16:06 -04001311 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001312 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001313 auto& ice = info->ice_;
1314 if (!ice) {
1315 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001316 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001317 return false;
1318 }
1319
1320 // Build socket
1321 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
1322 std::move(ice)),
1323 false);
1324
1325 // init TLS session
1326 auto ph = req.from;
1327 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001328 config_->logger->debug("[device {}] Start TLS session - Initied by DHT request. vid: {}",
1329 deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001330 req.id);
1331 info->tls_ = std::make_unique<TlsSocketEndpoint>(
1332 std::move(endpoint),
1333 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -04001334 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001335 identity(),
1336 dhParams(),
Adrien Béraud75754b22023-10-17 09:16:06 -04001337 [ph, deviceId, w=weak_from_this(), l=config_->logger](const dht::crypto::Certificate& cert) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001338 auto shared = w.lock();
1339 if (!shared)
1340 return false;
Adrien Béraud9efbd442023-08-27 12:38:07 -04001341 if (cert.getPublicKey().getId() != ph
1342 || deviceId != cert.getPublicKey().getLongId()) {
1343 if (l) l->warn("[device {}] TLS certificate with ID {} doesn't match the DHT request.",
1344 deviceId,
1345 cert.getPublicKey().getLongId());
1346 return false;
1347 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001348 auto crt = shared->certStore().getCertificate(cert.getLongId().toString());
1349 if (!crt)
1350 return false;
1351 return crt->getPacked() == cert.getPacked();
1352 });
1353
1354 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001355 [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 -04001356 if (auto shared = w.lock())
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -05001357 if (auto info = winfo.lock()) {
1358 shared->onTlsNegotiationDone(dinfo.lock(), winfo.lock(), ok, deviceId, vid);
1359 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
1360 dht::ThreadPool::io().run([info = std::move(info)] {});
1361 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001362 });
1363 return true;
1364}
1365
1366void
1367ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
1368 const std::shared_ptr<dht::crypto::Certificate>& /*cert*/)
1369{
1370 auto deviceId = req.owner->getLongId();
1371 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001372 config_->logger->debug("[device {}] New connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001373 if (!iceReqCb_ || !iceReqCb_(deviceId)) {
1374 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001375 config_->logger->debug("[device {}] Refusing connection", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001376 return;
1377 }
1378
1379 // Because the connection is accepted, create an ICE socket.
Adrien Béraud75754b22023-10-17 09:16:06 -04001380 getIceOptions([w = weak_from_this(), req, deviceId](auto&& ice_config) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001381 auto shared = w.lock();
1382 if (!shared)
1383 return;
Adrien Béraud75754b22023-10-17 09:16:06 -04001384
1385 auto di = shared->infos_.createDeviceInfo(deviceId);
1386 auto info = std::make_shared<ConnectionInfo>();
1387 auto wdi = std::weak_ptr(di);
1388 auto winfo = std::weak_ptr(info);
1389
Adrien Béraud612b55b2023-05-29 10:42:04 -04001390 // Note: used when the ice negotiation fails to erase
1391 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -04001392 auto eraseInfo = [w, wdi, id = req.id] {
1393 auto shared = w.lock();
1394 if (auto di = wdi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001395 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001396 di->info.erase(id);
1397 auto ops = di->extractPendingOperations(id, nullptr);
1398 if (di->empty()) {
1399 if (shared)
1400 shared->infos_.removeDeviceInfo(di->deviceId);
1401 }
1402 lk.unlock();
1403 for (const auto& op: ops)
1404 op.cb(nullptr, di->deviceId);
1405 if (shared && shared->connReadyCb_)
1406 shared->connReadyCb_(di->deviceId, "", nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001407 }
1408 };
1409
Adrien Béraud75754b22023-10-17 09:16:06 -04001410 ice_config.master = true;
1411 ice_config.streamsCount = 1;
1412 ice_config.compCountPerStream = 1; // TCP
Adrien Béraud612b55b2023-05-29 10:42:04 -04001413 ice_config.tcpEnable = true;
Adrien Béraud75754b22023-10-17 09:16:06 -04001414 ice_config.onInitDone = [w, 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)
ovari123a15c6882024-09-17 18:34:20 -04001420 shared->config_->logger->error("[device {}] Unable to initialize ICE session.", 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), winfo = std::move(winfo), req = std::move(req), eraseInfo = std::move(eraseInfo)] {
1427 if (auto shared = w.lock()) {
1428 if (!shared->onRequestStartIce(winfo.lock(), req))
1429 eraseInfo();
1430 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001431 });
1432 };
1433
Adrien Béraud75754b22023-10-17 09:16:06 -04001434 ice_config.onNegoDone = [w, wdi, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001435 auto shared = w.lock();
1436 if (!shared)
1437 return;
1438 if (!ok) {
1439 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001440 shared->config_->logger->error("[device {}] ICE negotiation failed.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001441 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1442 return;
1443 }
1444
1445 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001446 [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 -04001447 if (auto shared = w.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001448 if (!shared->onRequestOnNegoDone(wdi.lock(), winfo.lock(), req))
Adrien Béraud612b55b2023-05-29 10:42:04 -04001449 eraseInfo();
1450 });
1451 };
1452
1453 // Negotiate a new ICE socket
Adrien Béraud612b55b2023-05-29 10:42:04 -04001454 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001455 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001456 di->info[req.id] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001457 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001458
Adrien Béraud612b55b2023-05-29 10:42:04 -04001459 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001460 shared->config_->logger->debug("[device {}] Accepting connection", deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001461 std::unique_lock lk {info->mutex_};
Sébastien Blin34086512023-07-25 09:52:14 -04001462 info->ice_ = shared->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001463 if (not info->ice_) {
1464 if (shared->config_->logger)
ovari123a15c6882024-09-17 18:34:20 -04001465 shared->config_->logger->error("[device {}] Unable to initialize ICE session", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001466 eraseInfo();
1467 return;
1468 }
1469 // We need to detect any shutdown if the ice session is destroyed before going to the TLS session;
1470 info->ice_->setOnShutdown([eraseInfo]() {
1471 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1472 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001473 try {
1474 info->ice_->initIceInstance(ice_config);
1475 } catch (const std::exception& e) {
1476 if (shared->config_->logger)
1477 shared->config_->logger->error("{}", e.what());
1478 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1479 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001480 });
1481}
1482
1483void
Adrien Béraud75754b22023-10-17 09:16:06 -04001484ConnectionManager::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 -04001485{
Adrien Béraud75754b22023-10-17 09:16:06 -04001486 info->socket_ = std::make_shared<MultiplexedSocket>(config_->ioContext, deviceId, std::move(info->tls_), config_->logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001487 info->socket_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001488 [w = weak_from_this()](const DeviceId& deviceId, const std::shared_ptr<ChannelSocket>& socket) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001489 if (auto sthis = w.lock())
1490 if (sthis->connReadyCb_)
1491 sthis->connReadyCb_(deviceId, socket->name(), socket);
1492 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001493 info->socket_->setOnRequest([w = weak_from_this()](const std::shared_ptr<dht::crypto::Certificate>& peer,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001494 const uint16_t&,
1495 const std::string& name) {
1496 if (auto sthis = w.lock())
1497 if (sthis->channelReqCb_)
1498 return sthis->channelReqCb_(peer, name);
1499 return false;
1500 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001501 info->socket_->onShutdown([dinfo, wi=std::weak_ptr(info), vid]() {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001502 // Cancel current outgoing connections
Adrien Béraud75754b22023-10-17 09:16:06 -04001503 dht::ThreadPool::io().run([dinfo, wi, vid] {
1504 std::set<dht::Value::Id> ids;
1505 if (auto info = wi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001506 std::lock_guard lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001507 if (info->socket_) {
1508 ids = std::move(info->cbIds_);
1509 info->socket_->shutdown();
1510 }
1511 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001512 if (auto deviceInfo = dinfo.lock()) {
1513 std::shared_ptr<ConnectionInfo> info;
1514 std::vector<PendingCb> ops;
Adrien Béraud024c46f2024-03-02 23:53:18 -05001515 std::unique_lock lk(deviceInfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001516 auto it = deviceInfo->info.find(vid);
1517 if (it != deviceInfo->info.end()) {
1518 info = std::move(it->second);
1519 deviceInfo->info.erase(it);
1520 }
1521 for (const auto& cbId : ids) {
1522 auto po = deviceInfo->extractPendingOperations(cbId, nullptr);
1523 ops.insert(ops.end(), po.begin(), po.end());
1524 }
1525 lk.unlock();
1526 for (auto& op : ops)
1527 op.cb(nullptr, deviceInfo->deviceId);
1528 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001529 });
1530 });
1531}
1532
1533const std::shared_future<tls::DhParams>
1534ConnectionManager::Impl::dhParams() const
1535{
1536 return dht::ThreadPool::computation().get<tls::DhParams>(
Adrien Béraud2a4e73d2023-08-27 12:53:55 -04001537 std::bind(tls::DhParams::loadDhParams, config_->cachePath / "dhParams"));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001538}
1539
Adrien Béraud612b55b2023-05-29 10:42:04 -04001540bool
Adrien Béraud5aec4102024-02-22 14:15:56 -05001541ConnectionManager::Impl::isMessageTreated(dht::Value::Id id)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001542{
Adrien Béraud024c46f2024-03-02 23:53:18 -05001543 std::lock_guard lock(messageMutex_);
Adrien Béraud28e2ca52024-02-23 14:19:59 -05001544 return !treatedMessages_.add(id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001545}
1546
Adrien Béraud612b55b2023-05-29 10:42:04 -04001547
1548IpAddr
1549ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
1550{
1551 if (family == AF_INET)
1552 return publishedIp_[0];
1553 if (family == AF_INET6)
1554 return publishedIp_[1];
1555
1556 assert(family == AF_UNSPEC);
1557
1558 // If family is not set, prefere IPv4 if available. It's more
1559 // likely to succeed behind NAT.
1560 if (publishedIp_[0])
1561 return publishedIp_[0];
1562 if (publishedIp_[1])
1563 return publishedIp_[1];
1564 return {};
1565}
1566
1567void
1568ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr)
1569{
1570 if (ip_addr.getFamily() == AF_INET) {
1571 publishedIp_[0] = ip_addr;
1572 } else {
1573 publishedIp_[1] = ip_addr;
1574 }
1575}
1576
1577void
1578ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb)
1579{
Adrien Béraud75754b22023-10-17 09:16:06 -04001580 dht()->getPublicAddress([w=weak_from_this(), cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
Sébastien Blinb6504372023-10-12 10:35:35 -04001581 auto shared = w.lock();
1582 if (!shared)
1583 return;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001584 bool hasIpv4 {false}, hasIpv6 {false};
1585 for (auto& result : results) {
1586 auto family = result.getFamily();
1587 if (family == AF_INET) {
1588 if (not hasIpv4) {
1589 hasIpv4 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001590 if (shared->config_->logger)
1591 shared->config_->logger->debug("Store DHT public IPv4 address: {}", result);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001592 //JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str());
Sébastien Blinb6504372023-10-12 10:35:35 -04001593 shared->setPublishedAddress(*result.get());
1594 if (shared->config_->upnpCtrl) {
1595 shared->config_->upnpCtrl->setPublicAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001596 }
1597 }
1598 } else if (family == AF_INET6) {
1599 if (not hasIpv6) {
1600 hasIpv6 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001601 if (shared->config_->logger)
1602 shared->config_->logger->debug("Store DHT public IPv6 address: {}", result);
1603 shared->setPublishedAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001604 }
1605 }
1606 if (hasIpv4 and hasIpv6)
1607 break;
1608 }
1609 if (cb)
1610 cb();
1611 });
1612}
1613
1614void
1615ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1616{
1617 storeActiveIpAddress([this, cb = std::move(cb)] {
1618 IceTransportOptions opts = ConnectionManager::Impl::getIceOptions();
1619 auto publishedAddr = getPublishedIpAddress();
1620
1621 if (publishedAddr) {
1622 auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
1623 publishedAddr.getFamily());
1624 if (interfaceAddr) {
1625 opts.accountLocalAddr = interfaceAddr;
1626 opts.accountPublicAddr = publishedAddr;
1627 }
1628 }
1629 if (cb)
1630 cb(std::move(opts));
1631 });
1632}
1633
1634IceTransportOptions
1635ConnectionManager::Impl::getIceOptions() const noexcept
1636{
1637 IceTransportOptions opts;
Sébastien Blin34086512023-07-25 09:52:14 -04001638 opts.factory = config_->factory;
Amna49da80f2024-08-26 16:54:40 -04001639 opts.upnpEnable = config_->upnpEnabled;
Adrien Béraud7b869d92023-08-21 09:02:35 -04001640 opts.upnpContext = config_->upnpCtrl ? config_->upnpCtrl->upnpContext() : nullptr;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001641
1642 if (config_->stunEnabled)
1643 opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer));
1644 if (config_->turnEnabled) {
Sébastien Blin84bf4182023-07-21 14:18:39 -04001645 if (config_->turnCache) {
1646 auto turnAddr = config_->turnCache->getResolvedTurn();
1647 if (turnAddr != std::nullopt) {
1648 opts.turnServers.emplace_back(TurnServerInfo()
1649 .setUri(turnAddr->toString())
1650 .setUsername(config_->turnServerUserName)
1651 .setPassword(config_->turnServerPwd)
1652 .setRealm(config_->turnServerRealm));
1653 }
1654 } else {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001655 opts.turnServers.emplace_back(TurnServerInfo()
Sébastien Blin84bf4182023-07-21 14:18:39 -04001656 .setUri(config_->turnServer)
1657 .setUsername(config_->turnServerUserName)
1658 .setPassword(config_->turnServerPwd)
1659 .setRealm(config_->turnServerRealm));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001660 }
1661 // NOTE: first test with ipv6 turn was not concluant and resulted in multiple
1662 // co issues. So this needs some debug. for now just disable
1663 // if (cacheTurnV6 && *cacheTurnV6) {
1664 // opts.turnServers.emplace_back(TurnServerInfo()
1665 // .setUri(cacheTurnV6->toString(true))
1666 // .setUsername(turnServerUserName_)
1667 // .setPassword(turnServerPwd_)
1668 // .setRealm(turnServerRealm_));
1669 //}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001670 }
1671 return opts;
1672}
1673
1674bool
1675ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
1676 dht::InfoHash& account_id,
1677 const std::shared_ptr<Logger>& logger)
1678{
1679 if (not crt)
1680 return false;
1681
1682 auto top_issuer = crt;
1683 while (top_issuer->issuer)
1684 top_issuer = top_issuer->issuer;
1685
ovari123a15c6882024-09-17 18:34:20 -04001686 // Unable to self-signed device certificate
Adrien Béraudc631a832023-07-26 22:19:00 -04001687 if (top_issuer == crt) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001688 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001689 logger->warn("Found invalid (self-signed) peer device: {}", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001690 return false;
Adrien Béraudc631a832023-07-26 22:19:00 -04001691 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001692
1693 // Check peer certificate chain
1694 // Trust store with top issuer as the only CA
1695 dht::crypto::TrustList peer_trust;
1696 peer_trust.add(*top_issuer);
1697 if (not peer_trust.verify(*crt)) {
1698 if (logger)
1699 logger->warn("Found invalid peer device: {}", crt->getLongId());
1700 return false;
1701 }
1702
1703 // Check cached OCSP response
1704 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
1705 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001706 logger->error("Certificate {} is disabled by cached OCSP response", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001707 return false;
1708 }
1709
Adrien Béraudc631a832023-07-26 22:19:00 -04001710 account_id = crt->issuer->getId();
1711 if (logger)
1712 logger->warn("Found peer device: {} account:{} CA:{}",
1713 crt->getLongId(),
1714 account_id,
1715 top_issuer->getId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001716 return true;
1717}
1718
1719bool
1720ConnectionManager::Impl::findCertificate(
1721 const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1722{
1723 if (auto cert = certStore().getCertificate(id.toString())) {
1724 if (cb)
1725 cb(cert);
1726 } else if (cb)
1727 cb(nullptr);
1728 return true;
1729}
1730
Sébastien Blin34086512023-07-25 09:52:14 -04001731bool
1732ConnectionManager::Impl::findCertificate(const dht::InfoHash& h,
1733 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1734{
1735 if (auto cert = certStore().getCertificate(h.toString())) {
1736 if (cb)
1737 cb(cert);
1738 } else {
1739 dht()->findCertificate(h,
1740 [cb = std::move(cb), this](
1741 const std::shared_ptr<dht::crypto::Certificate>& crt) {
1742 if (crt)
1743 certStore().pinCertificate(crt);
1744 if (cb)
1745 cb(crt);
1746 });
1747 }
1748 return true;
1749}
1750
Amna81221ad2023-09-14 17:33:26 -04001751std::shared_ptr<ConnectionManager::Config>
1752buildDefaultConfig(dht::crypto::Identity id){
1753 auto conf = std::make_shared<ConnectionManager::Config>();
1754 conf->id = std::move(id);
1755 return conf;
1756}
1757
Adrien Béraud612b55b2023-05-29 10:42:04 -04001758ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_)
1759 : pimpl_ {std::make_shared<Impl>(config_)}
1760{}
1761
Amna81221ad2023-09-14 17:33:26 -04001762ConnectionManager::ConnectionManager(dht::crypto::Identity id)
1763 : ConnectionManager {buildDefaultConfig(id)}
1764{}
1765
Adrien Béraud612b55b2023-05-29 10:42:04 -04001766ConnectionManager::~ConnectionManager()
1767{
1768 if (pimpl_)
1769 pimpl_->shutdown();
1770}
1771
1772void
1773ConnectionManager::connectDevice(const DeviceId& deviceId,
1774 const std::string& name,
1775 ConnectCallback cb,
1776 bool noNewSocket,
1777 bool forceNewSocket,
1778 const std::string& connType)
1779{
1780 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1781}
1782
1783void
Amna0cf544d2023-07-25 14:25:09 -04001784ConnectionManager::connectDevice(const dht::InfoHash& deviceId,
1785 const std::string& name,
1786 ConnectCallbackLegacy cb,
1787 bool noNewSocket,
1788 bool forceNewSocket,
1789 const std::string& connType)
1790{
1791 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1792}
1793
1794
1795void
Adrien Béraud612b55b2023-05-29 10:42:04 -04001796ConnectionManager::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
1797 const std::string& name,
1798 ConnectCallback cb,
1799 bool noNewSocket,
1800 bool forceNewSocket,
1801 const std::string& connType)
1802{
1803 pimpl_->connectDevice(cert, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1804}
1805
1806bool
1807ConnectionManager::isConnecting(const DeviceId& deviceId, const std::string& name) const
1808{
Adrien Béraud75754b22023-10-17 09:16:06 -04001809 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001810 std::unique_lock lk {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001811 return dinfo->isConnecting(name);
Adrien Béraud75754b22023-10-17 09:16:06 -04001812 }
1813 return false;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001814}
1815
Sébastien Blind0c92c72023-12-07 15:27:51 -05001816bool
1817ConnectionManager::isConnected(const DeviceId& deviceId) const
1818{
1819 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001820 std::unique_lock lk {dinfo->mtx_};
Sébastien Blind0c92c72023-12-07 15:27:51 -05001821 return dinfo->getConnectedInfo() != nullptr;
1822 }
1823 return false;
1824}
1825
Adrien Béraud612b55b2023-05-29 10:42:04 -04001826void
1827ConnectionManager::closeConnectionsWith(const std::string& peerUri)
1828{
Adrien Béraud75754b22023-10-17 09:16:06 -04001829 std::vector<std::shared_ptr<DeviceInfo>> dInfos;
1830 for (const auto& dinfo: pimpl_->infos_.getDeviceInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001831 std::unique_lock lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001832 bool isPeer = false;
1833 for (auto const& [id, cinfo]: dinfo->info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001834 std::lock_guard lkv {cinfo->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001835 auto tls = cinfo->tls_ ? cinfo->tls_.get() : (cinfo->socket_ ? cinfo->socket_->endpoint() : nullptr);
Adrien Béraudafa8e282023-09-24 12:53:20 -04001836 auto cert = tls ? tls->peerCertificate() : nullptr;
1837 if (not cert)
Adrien Béraud75754b22023-10-17 09:16:06 -04001838 cert = pimpl_->certStore().getCertificate(dinfo->deviceId.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001839 if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001840 isPeer = true;
1841 break;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001842 }
1843 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001844 lk.unlock();
1845 if (isPeer) {
1846 dInfos.emplace_back(std::move(dinfo));
1847 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001848 }
1849 // Stop connections to all peers devices
Adrien Béraud75754b22023-10-17 09:16:06 -04001850 for (const auto& dinfo : dInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001851 std::unique_lock lk {dinfo->mtx_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001852 auto unused = dinfo->extractUnusedConnections();
1853 auto pending = dinfo->extractPendingOperations(0, nullptr);
1854 pimpl_->infos_.removeDeviceInfo(dinfo->deviceId);
1855 lk.unlock();
1856 for (auto& op : unused)
1857 op->shutdown();
1858 for (auto& op : pending)
1859 op.cb(nullptr, dinfo->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001860 }
1861}
1862
1863void
1864ConnectionManager::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1865{
1866 pimpl_->onDhtConnected(devicePk);
1867}
1868
1869void
1870ConnectionManager::onICERequest(onICERequestCallback&& cb)
1871{
1872 pimpl_->iceReqCb_ = std::move(cb);
1873}
1874
1875void
1876ConnectionManager::onChannelRequest(ChannelRequestCallback&& cb)
1877{
1878 pimpl_->channelReqCb_ = std::move(cb);
1879}
1880
1881void
1882ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb)
1883{
1884 pimpl_->connReadyCb_ = std::move(cb);
1885}
1886
1887void
1888ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb)
1889{
1890 pimpl_->iOSConnectedCb_ = std::move(cb);
1891}
1892
1893std::size_t
1894ConnectionManager::activeSockets() const
1895{
Adrien Béraud75754b22023-10-17 09:16:06 -04001896 return pimpl_->infos_.getConnectedInfos().size();
Adrien Béraud612b55b2023-05-29 10:42:04 -04001897}
1898
1899void
1900ConnectionManager::monitor() const
1901{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001902 auto logger = pimpl_->config_->logger;
1903 if (!logger)
1904 return;
1905 logger->debug("ConnectionManager current status:");
Adrien Béraud75754b22023-10-17 09:16:06 -04001906 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001907 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001908 if (ci->socket_)
1909 ci->socket_->monitor();
1910 }
1911 logger->debug("ConnectionManager end status.");
1912}
1913
1914void
1915ConnectionManager::connectivityChanged()
1916{
Adrien Béraud75754b22023-10-17 09:16:06 -04001917 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001918 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001919 if (ci->socket_)
Adrien Béraud51a54712023-10-17 21:24:30 -04001920 dht::ThreadPool::io().run([s = ci->socket_] { s->sendBeacon(); });
Adrien Béraud612b55b2023-05-29 10:42:04 -04001921 }
1922}
1923
1924void
1925ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1926{
1927 return pimpl_->getIceOptions(std::move(cb));
1928}
1929
1930IceTransportOptions
1931ConnectionManager::getIceOptions() const noexcept
1932{
1933 return pimpl_->getIceOptions();
1934}
1935
1936IpAddr
1937ConnectionManager::getPublishedIpAddress(uint16_t family) const
1938{
1939 return pimpl_->getPublishedIpAddress(family);
1940}
1941
1942void
1943ConnectionManager::setPublishedAddress(const IpAddr& ip_addr)
1944{
1945 return pimpl_->setPublishedAddress(ip_addr);
1946}
1947
1948void
1949ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb)
1950{
1951 return pimpl_->storeActiveIpAddress(std::move(cb));
1952}
1953
1954std::shared_ptr<ConnectionManager::Config>
1955ConnectionManager::getConfig()
1956{
1957 return pimpl_->config_;
1958}
1959
Amna31791e52023-08-03 12:40:57 -04001960std::vector<std::map<std::string, std::string>>
1961ConnectionManager::getConnectionList(const DeviceId& device) const
1962{
1963 std::vector<std::map<std::string, std::string>> connectionsList;
Amna31791e52023-08-03 12:40:57 -04001964 if (device) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001965 if (auto deviceInfo = pimpl_->infos_.getDeviceInfo(device)) {
1966 connectionsList = deviceInfo->getConnectionList(pimpl_->certStore());
Amna31791e52023-08-03 12:40:57 -04001967 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001968 } else {
1969 for (const auto& deviceInfo : pimpl_->infos_.getDeviceInfos()) {
1970 auto cl = deviceInfo->getConnectionList(pimpl_->certStore());
1971 connectionsList.insert(connectionsList.end(), std::make_move_iterator(cl.begin()), std::make_move_iterator(cl.end()));
Amna31791e52023-08-03 12:40:57 -04001972 }
1973 }
1974 return connectionsList;
1975}
1976
1977std::vector<std::map<std::string, std::string>>
1978ConnectionManager::getChannelList(const std::string& connectionId) const
1979{
Adrien Béraud75754b22023-10-17 09:16:06 -04001980 auto [deviceId, valueId] = parseCallbackId(connectionId);
1981 if (auto info = pimpl_->infos_.getInfo(deviceId, valueId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001982 std::lock_guard lk(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001983 if (info->socket_)
1984 return info->socket_->getChannelList();
Amna31791e52023-08-03 12:40:57 -04001985 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001986 return {};
Amna31791e52023-08-03 12:40:57 -04001987}
1988
Sébastien Blin464bdff2023-07-19 08:02:53 -04001989} // namespace dhtnet