blob: c0bde96c51942a88c7be53ece5b3c4ffc07f172d [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
Adrien Béraudcb753622023-07-17 22:32:49 -04002 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
Adrien Béraud612b55b2023-05-29 10:42:04 -04003 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "connectionmanager.h"
18#include "peer_connection.h"
19#include "upnp/upnp_control.h"
20#include "certstore.h"
21#include "fileutils.h"
22#include "sip_utils.h"
23#include "string_utils.h"
24
25#include <opendht/crypto.h>
26#include <opendht/thread_pool.h>
27#include <opendht/value.h>
28#include <asio.hpp>
29
30#include <algorithm>
31#include <mutex>
32#include <map>
33#include <condition_variable>
34#include <set>
35#include <charconv>
Morteza Namvar5f639522023-07-04 17:08:58 -040036#include <fstream>
Adrien Béraud612b55b2023-05-29 10:42:04 -040037
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040038namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040039static constexpr std::chrono::seconds DHT_MSG_TIMEOUT {30};
40static constexpr uint64_t ID_MAX_VAL = 9007199254740992;
41
42using ValueIdDist = std::uniform_int_distribution<dht::Value::Id>;
Adrien Béraud75754b22023-10-17 09:16:06 -040043
Amna31791e52023-08-03 12:40:57 -040044std::string
45callbackIdToString(const dhtnet::DeviceId& did, const dht::Value::Id& vid)
46{
47 return fmt::format("{} {}", did.to_view(), vid);
48}
Adrien Béraud612b55b2023-05-29 10:42:04 -040049
Adrien Béraud75754b22023-10-17 09:16:06 -040050std::pair<dhtnet::DeviceId, dht::Value::Id> parseCallbackId(std::string_view ci)
Amna31791e52023-08-03 12:40:57 -040051{
52 auto sep = ci.find(' ');
53 std::string_view deviceIdString = ci.substr(0, sep);
54 std::string_view vidString = ci.substr(sep + 1);
55
56 dhtnet::DeviceId deviceId(deviceIdString);
Andreas Traczyk0da84a72024-04-30 20:40:14 -040057 dht::Value::Id vid = std::stoull(std::string(vidString), nullptr, 10);
Adrien Béraud75754b22023-10-17 09:16:06 -040058 return {deviceId, vid};
Amna31791e52023-08-03 12:40:57 -040059}
Amna81221ad2023-09-14 17:33:26 -040060
61std::shared_ptr<ConnectionManager::Config>
62createConfig(std::shared_ptr<ConnectionManager::Config> config_)
63{
64 if (!config_->certStore){
65 config_->certStore = std::make_shared<dhtnet::tls::CertificateStore>("client", config_->logger);
66 }
67 if (!config_->dht) {
68 dht::DhtRunner::Config dhtConfig;
69 dhtConfig.dht_config.id = config_->id;
70 dhtConfig.threaded = true;
71 dht::DhtRunner::Context dhtContext;
72 dhtContext.certificateStore = [c = config_->certStore](const dht::InfoHash& pk_id) {
73 std::vector<std::shared_ptr<dht::crypto::Certificate>> ret;
74 if (auto cert = c->getCertificate(pk_id.toString()))
75 ret.emplace_back(std::move(cert));
76 return ret;
77 };
78 config_->dht = std::make_shared<dht::DhtRunner>();
79 config_->dht->run(dhtConfig, std::move(dhtContext));
80 config_->dht->bootstrap("bootstrap.jami.net");
81 }
82 if (!config_->factory){
83 config_->factory = std::make_shared<IceTransportFactory>(config_->logger);
84 }
85 return config_;
86}
87
Adrien Béraud612b55b2023-05-29 10:42:04 -040088struct ConnectionInfo
89{
90 ~ConnectionInfo()
91 {
92 if (socket_)
93 socket_->join();
94 }
95
96 std::mutex mutex_ {};
97 bool responseReceived_ {false};
98 PeerConnectionRequest response_ {};
99 std::unique_ptr<IceTransport> ice_ {nullptr};
100 // Used to store currently non ready TLS Socket
101 std::unique_ptr<TlsSocketEndpoint> tls_ {nullptr};
102 std::shared_ptr<MultiplexedSocket> socket_ {};
Adrien Béraud75754b22023-10-17 09:16:06 -0400103 std::set<dht::Value::Id> cbIds_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400104
105 std::function<void(bool)> onConnected_;
106 std::unique_ptr<asio::steady_timer> waitForAnswer_ {};
Adrien Béraud75754b22023-10-17 09:16:06 -0400107
108 void shutdown() {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500109 std::lock_guard lk(mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400110 if (tls_)
111 tls_->shutdown();
112 if (socket_)
113 socket_->shutdown();
114 if (waitForAnswer_)
115 waitForAnswer_->cancel();
116 if (ice_) {
117 dht::ThreadPool::io().run(
118 [ice = std::shared_ptr<IceTransport>(std::move(ice_))] {});
119 }
120 }
121
122 std::map<std::string, std::string>
123 getInfo(const DeviceId& deviceId, dht::Value::Id valueId, tls::CertificateStore& certStore) const
124 {
125 std::map<std::string, std::string> connectionInfo;
126 connectionInfo["id"] = callbackIdToString(deviceId, valueId);
127 connectionInfo["device"] = deviceId.toString();
128 auto cert = tls_ ? tls_->peerCertificate() : (socket_ ? socket_->peerCertificate() : nullptr);
129 if (not cert)
130 cert = certStore.getCertificate(deviceId.toString());
131 if (cert) {
132 connectionInfo["peer"] = cert->issuer->getId().toString();
133 }
134 if (socket_) {
135 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::Connected));
136 connectionInfo["remoteAddress"] = socket_->getRemoteAddress();
137 } else if (tls_) {
138 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::TLS));
139 connectionInfo["remoteAddress"] = tls_->getRemoteAddress();
140 } else if(ice_) {
141 connectionInfo["status"] = std::to_string(static_cast<int>(ConnectionStatus::ICE));
142 connectionInfo["remoteAddress"] = ice_->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
143 }
144 return connectionInfo;
145 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400146};
147
Adrien Béraud75754b22023-10-17 09:16:06 -0400148struct PendingCb {
149 std::string name;
150 ConnectCallback cb;
Adrien Béraudb941e922023-10-16 12:56:14 -0400151 bool requested {false};
Adrien Béraud75754b22023-10-17 09:16:06 -0400152};
153
154struct DeviceInfo {
155 const DeviceId deviceId;
156 mutable std::mutex mtx_ {};
157 std::map<dht::Value::Id, std::shared_ptr<ConnectionInfo>> info;
158 std::map<dht::Value::Id, PendingCb> connecting;
159 std::map<dht::Value::Id, PendingCb> waiting;
160 DeviceInfo(DeviceId id) : deviceId {id} {}
161
162 inline bool isConnecting() const {
163 return !connecting.empty() || !waiting.empty();
164 }
165
166 inline bool empty() const {
167 return info.empty() && connecting.empty() && waiting.empty();
168 }
169
170 dht::Value::Id newId(std::mt19937_64& rand) const {
171 ValueIdDist dist(1, ID_MAX_VAL);
172 dht::Value::Id id;
173 do {
174 id = dist(rand);
175 } while (info.find(id) != info.end()
176 || connecting.find(id) != connecting.end()
177 || waiting.find(id) != waiting.end());
178 return id;
179 }
180
181 std::shared_ptr<ConnectionInfo> getConnectedInfo() const {
182 for (auto& [id, ci] : info) {
183 if (ci->socket_)
184 return ci;
185 }
186 return {};
187 }
188
189 std::vector<PendingCb> extractPendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true)
190 {
191 std::vector<PendingCb> ret;
192 if (vid == 0) {
193 // Extract all pending callbacks
194 ret.reserve(connecting.size() + waiting.size());
195 for (auto& [vid, cb] : connecting)
196 ret.emplace_back(std::move(cb));
197 connecting.clear();
198 for (auto& [vid, cb] : waiting)
199 ret.emplace_back(std::move(cb));
200 waiting.clear();
201 } else if (auto n = waiting.extract(vid)) {
202 // If it's a waiting operation, just move it
203 ret.emplace_back(std::move(n.mapped()));
204 } else if (auto n = connecting.extract(vid)) {
205 ret.emplace_back(std::move(n.mapped()));
206 // If sock is nullptr, execute if it's the last connecting operation
207 // If accepted is false, it means that underlying socket is ok, but channel is declined
208 if (!sock && connecting.empty() && accepted) {
209 for (auto& [vid, cb] : waiting)
210 ret.emplace_back(std::move(cb));
211 waiting.clear();
212 for (auto& [vid, cb] : connecting)
213 ret.emplace_back(std::move(cb));
214 connecting.clear();
215 }
216 }
217 return ret;
218 }
219
220 std::vector<std::shared_ptr<ConnectionInfo>> extractUnusedConnections() {
221 std::vector<std::shared_ptr<ConnectionInfo>> unused {};
222 for (auto& [id, info] : info)
223 unused.emplace_back(std::move(info));
224 info.clear();
225 return unused;
226 }
227
228 void executePendingOperations(std::unique_lock<std::mutex>& lock, dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
229 auto ops = extractPendingOperations(vid, sock, accepted);
230 lock.unlock();
231 for (auto& cb : ops)
232 cb.cb(sock, deviceId);
233 }
234 void executePendingOperations(dht::Value::Id vid, const std::shared_ptr<ChannelSocket>& sock, bool accepted = true) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500235 std::unique_lock lock(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400236 executePendingOperations(lock, vid, sock, accepted);
237 }
238
Adrien Béraudb941e922023-10-16 12:56:14 -0400239 bool isConnecting(const std::string& name) const {
Adrien Béraud75754b22023-10-17 09:16:06 -0400240 for (const auto& [id, pc]: connecting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400241 if (pc.name == name)
242 return true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400243 for (const auto& [id, pc]: waiting)
Adrien Béraudb941e922023-10-16 12:56:14 -0400244 if (pc.name == name)
245 return true;
246 return false;
247 }
248 std::map<dht::Value::Id, std::string> requestPendingOps() {
249 std::map<dht::Value::Id, std::string> ret;
250 for (auto& [id, pc]: connecting) {
251 if (!pc.requested) {
252 ret[id] = pc.name;
253 pc.requested = true;
254 }
255 }
256 for (auto& [id, pc]: waiting) {
257 if (!pc.requested) {
258 ret[id] = pc.name;
259 pc.requested = true;
260 }
261 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400262 return ret;
263 }
264
265 std::vector<std::map<std::string, std::string>>
266 getConnectionList(tls::CertificateStore& certStore) const {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500267 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400268 std::vector<std::map<std::string, std::string>> ret;
Adrien Béraudd5ec7a82023-10-28 18:07:03 -0400269 ret.reserve(info.size() + connecting.size() + waiting.size());
Adrien Béraud75754b22023-10-17 09:16:06 -0400270 for (auto& [id, ci] : info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500271 std::lock_guard lk(ci->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400272 ret.emplace_back(ci->getInfo(deviceId, id, certStore));
273 }
274 auto cert = certStore.getCertificate(deviceId.toString());
275 for (const auto& [vid, ci] : connecting) {
276 ret.emplace_back(std::map<std::string, std::string> {
277 {"id", callbackIdToString(deviceId, vid)},
278 {"status", std::to_string(static_cast<int>(ConnectionStatus::Connecting))},
279 {"device", deviceId.toString()},
280 {"peer", cert ? cert->issuer->getId().toString() : ""}
281 });
282 }
283 for (const auto& [vid, ci] : waiting) {
284 ret.emplace_back(std::map<std::string, std::string> {
285 {"id", callbackIdToString(deviceId, vid)},
286 {"status", std::to_string(static_cast<int>(ConnectionStatus::Waiting))},
287 {"device", deviceId.toString()},
288 {"peer", cert ? cert->issuer->getId().toString() : ""}
289 });
290 }
291 return ret;
292 }
293};
294
295class DeviceInfoSet {
296public:
297 std::shared_ptr<DeviceInfo> getDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500298 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400299 auto it = infos_.find(deviceId);
300 if (it != infos_.end())
301 return it->second;
302 return {};
303 }
304
305 std::vector<std::shared_ptr<DeviceInfo>> getDeviceInfos() {
306 std::vector<std::shared_ptr<DeviceInfo>> deviceInfos;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500307 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400308 deviceInfos.reserve(infos_.size());
309 for (auto& [deviceId, info] : infos_)
310 deviceInfos.emplace_back(info);
311 return deviceInfos;
312 }
313
314 std::shared_ptr<DeviceInfo> createDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500315 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400316 auto& info = infos_[deviceId];
317 if (!info)
318 info = std::make_shared<DeviceInfo>(deviceId);
319 return info;
320 }
321
322 bool removeDeviceInfo(const DeviceId& deviceId) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500323 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400324 return infos_.erase(deviceId) != 0;
325 }
326
327 std::shared_ptr<ConnectionInfo> getInfo(const DeviceId& deviceId, const dht::Value::Id& id) {
328 if (auto info = getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500329 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400330 auto it = info->info.find(id);
331 if (it != info->info.end())
332 return it->second;
333 }
334 return {};
335 }
336
337 std::vector<std::shared_ptr<ConnectionInfo>> getConnectedInfos() {
338 auto deviceInfos = getDeviceInfos();
339 std::vector<std::shared_ptr<ConnectionInfo>> ret;
340 ret.reserve(deviceInfos.size());
341 for (auto& info : deviceInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500342 std::lock_guard lk(info->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400343 for (auto& [id, ci] : info->info) {
344 if (ci->socket_)
345 ret.emplace_back(ci);
346 }
347 }
348 return ret;
349 }
350 std::vector<std::shared_ptr<DeviceInfo>> shutdown() {
351 std::vector<std::shared_ptr<DeviceInfo>> ret;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500352 std::lock_guard lk(mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400353 ret.reserve(infos_.size());
354 for (auto& [deviceId, info] : infos_) {
355 ret.emplace_back(std::move(info));
356 }
357 infos_.clear();
358 return ret;
359 }
360
361private:
362 std::mutex mtx_ {};
363 std::map<DeviceId, std::shared_ptr<DeviceInfo>> infos_ {};
364};
365
366
Adrien Béraud612b55b2023-05-29 10:42:04 -0400367/**
368 * returns whether or not UPnP is enabled and active_
369 * ie: if it is able to make port mappings
370 */
371bool
372ConnectionManager::Config::getUPnPActive() const
373{
374 if (upnpCtrl)
375 return upnpCtrl->isReady();
376 return false;
377}
378
379class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl>
380{
381public:
382 explicit Impl(std::shared_ptr<ConnectionManager::Config> config_)
Amna81221ad2023-09-14 17:33:26 -0400383 : config_ {std::move(createConfig(config_))}
Adrien Béraudd8b6a402023-12-08 14:19:25 -0500384 , rand_ {config_->rng ? *config_->rng : dht::crypto::getSeededRandomEngine<std::mt19937_64>()}
Adrien Béraud5aec4102024-02-22 14:15:56 -0500385 , treatedMessages_ {config_->cachePath / "treatedMessages"}
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400386 {
Amna81221ad2023-09-14 17:33:26 -0400387 if(!config_->ioContext) {
388 config_->ioContext = std::make_shared<asio::io_context>();
389 ioContextRunner_ = std::make_unique<std::thread>([context = config_->ioContext, l=config_->logger]() {
390 try {
391 auto work = asio::make_work_guard(*context);
392 context->run();
393 } catch (const std::exception& ex) {
394 if (l) l->error("Exception: {}", ex.what());
395 }
396 });
397 }
Kateryna Kostiukc39f6672023-09-15 16:49:42 -0400398 }
Amna81221ad2023-09-14 17:33:26 -0400399 ~Impl() {
400 if (ioContextRunner_) {
401 if (config_->logger) config_->logger->debug("ConnectionManager: stopping io_context thread");
402 config_->ioContext->stop();
403 ioContextRunner_->join();
404 ioContextRunner_.reset();
405 }
406 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400407
408 std::shared_ptr<dht::DhtRunner> dht() { return config_->dht; }
409 const dht::crypto::Identity& identity() const { return config_->id; }
410
Adrien Béraud75754b22023-10-17 09:16:06 -0400411 void shutdown()
Adrien Béraud612b55b2023-05-29 10:42:04 -0400412 {
Adrien Béraud75754b22023-10-17 09:16:06 -0400413 if (isDestroying_.exchange(true))
414 return;
415 std::vector<std::shared_ptr<ConnectionInfo>> unused;
416 std::vector<std::pair<DeviceId, std::vector<PendingCb>>> pending;
417 for (auto& dinfo: infos_.shutdown()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500418 std::lock_guard lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400419 auto p = dinfo->extractPendingOperations(0, nullptr, false);
420 if (!p.empty())
421 pending.emplace_back(dinfo->deviceId, std::move(p));
422 auto uc = dinfo->extractUnusedConnections();
423 unused.insert(unused.end(), std::make_move_iterator(uc.begin()), std::make_move_iterator(uc.end()));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400424 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400425 for (auto& info: unused)
426 info->shutdown();
427 for (auto& op: pending)
428 for (auto& cb: op.second)
429 cb.cb(nullptr, op.first);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400430 if (!unused.empty())
Amna81221ad2023-09-14 17:33:26 -0400431 dht::ThreadPool::io().run([infos = std::move(unused)]() mutable {
432 infos.clear();
433 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400434 }
435
Adrien Béraud75754b22023-10-17 09:16:06 -0400436 void connectDeviceStartIce(const std::shared_ptr<ConnectionInfo>& info,
437 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400438 const dht::Value::Id& vid,
439 const std::string& connType,
440 std::function<void(bool)> onConnected);
Adrien Béraud75754b22023-10-17 09:16:06 -0400441 void onResponse(const asio::error_code& ec, const std::weak_ptr<ConnectionInfo>& info, const DeviceId& deviceId, const dht::Value::Id& vid);
442 bool connectDeviceOnNegoDone(const std::weak_ptr<DeviceInfo>& dinfo,
443 const std::shared_ptr<ConnectionInfo>& info,
444 const DeviceId& deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400445 const std::string& name,
446 const dht::Value::Id& vid,
447 const std::shared_ptr<dht::crypto::Certificate>& cert);
448 void connectDevice(const DeviceId& deviceId,
449 const std::string& uri,
450 ConnectCallback cb,
451 bool noNewSocket = false,
452 bool forceNewSocket = false,
453 const std::string& connType = "");
Amna0cf544d2023-07-25 14:25:09 -0400454 void connectDevice(const dht::InfoHash& deviceId,
455 const std::string& uri,
456 ConnectCallbackLegacy cb,
457 bool noNewSocket = false,
458 bool forceNewSocket = false,
459 const std::string& connType = "");
460
Adrien Béraud612b55b2023-05-29 10:42:04 -0400461 void connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
462 const std::string& name,
463 ConnectCallback cb,
464 bool noNewSocket = false,
465 bool forceNewSocket = false,
466 const std::string& connType = "");
467 /**
468 * Send a ChannelRequest on the TLS socket. Triggers cb when ready
469 * @param sock socket used to send the request
470 * @param name channel's name
471 * @param vid channel's id
472 * @param deviceId to identify the linked ConnectCallback
473 */
Adrien Béraud75754b22023-10-17 09:16:06 -0400474 void sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfo,
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400475 const std::weak_ptr<ConnectionInfo>& cinfo,
Adrien Béraud75754b22023-10-17 09:16:06 -0400476 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400477 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400478 const dht::Value::Id& vid);
479 /**
480 * Triggered when a PeerConnectionRequest comes from the DHT
481 */
482 void answerTo(IceTransport& ice,
483 const dht::Value::Id& id,
484 const std::shared_ptr<dht::crypto::PublicKey>& fromPk);
Adrien Béraud75754b22023-10-17 09:16:06 -0400485 bool onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req);
486 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 -0400487 void onDhtPeerRequest(const PeerConnectionRequest& req,
488 const std::shared_ptr<dht::crypto::Certificate>& cert);
Adrien Béraud75754b22023-10-17 09:16:06 -0400489 /**
490 * Triggered when a new TLS socket is ready to use
491 * @param ok If succeed
492 * @param deviceId Related device
493 * @param vid vid of the connection request
494 * @param name non empty if TLS was created by connectDevice()
495 */
496 void onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
497 const std::shared_ptr<ConnectionInfo>& info,
498 bool ok,
499 const DeviceId& deviceId,
500 const dht::Value::Id& vid,
501 const std::string& name = "");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400502
Adrien Béraud75754b22023-10-17 09:16:06 -0400503 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 -0400504 void onPeerResponse(PeerConnectionRequest&& req);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400505 void onDhtConnected(const dht::crypto::PublicKey& devicePk);
506
Adrien Béraud75754b22023-10-17 09:16:06 -0400507
Adrien Béraud612b55b2023-05-29 10:42:04 -0400508 const std::shared_future<tls::DhParams> dhParams() const;
509 tls::CertificateStore& certStore() const { return *config_->certStore; }
510
511 mutable std::mutex messageMutex_ {};
Adrien Béraud5aec4102024-02-22 14:15:56 -0500512 fileutils::IdList treatedMessages_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400513
514 /// \return true if the given DHT message identifier has been treated
515 /// \note if message has not been treated yet this method st/ore this id and returns true at
516 /// further calls
Adrien Béraud5aec4102024-02-22 14:15:56 -0500517 bool isMessageTreated(dht::Value::Id id);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400518
519 const std::shared_ptr<dht::log::Logger>& logger() const { return config_->logger; }
520
521 /**
522 * Published IPv4/IPv6 addresses, used only if defined by the user in account
523 * configuration
524 *
525 */
526 IpAddr publishedIp_[2] {};
527
Adrien Béraud612b55b2023-05-29 10:42:04 -0400528 /**
529 * interface name on which this account is bound
530 */
531 std::string interface_ {"default"};
532
533 /**
534 * Get the local interface name on which this account is bound.
535 */
536 const std::string& getLocalInterface() const { return interface_; }
537
538 /**
539 * Get the published IP address, fallbacks to NAT if family is unspecified
540 * Prefers the usage of IPv4 if possible.
541 */
542 IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
543
544 /**
545 * Set published IP address according to given family
546 */
547 void setPublishedAddress(const IpAddr& ip_addr);
548
549 /**
550 * Store the local/public addresses used to register
551 */
552 void storeActiveIpAddress(std::function<void()>&& cb = {});
553
554 /**
555 * Create and return ICE options.
556 */
557 void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
558 IceTransportOptions getIceOptions() const noexcept;
559
560 /**
561 * Inform that a potential peer device have been found.
562 * Returns true only if the device certificate is a valid device certificate.
563 * In that case (true is returned) the account_id parameter is set to the peer account ID.
564 */
565 static bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
566 dht::InfoHash& account_id, const std::shared_ptr<Logger>& logger);
567
568 bool findCertificate(const dht::PkId& id,
569 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
Sébastien Blin34086512023-07-25 09:52:14 -0400570 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 -0400571
572 /**
573 * returns whether or not UPnP is enabled and active
574 * ie: if it is able to make port mappings
575 */
576 bool getUPnPActive() const;
577
Adrien Béraud612b55b2023-05-29 10:42:04 -0400578 std::shared_ptr<ConnectionManager::Config> config_;
Amna81221ad2023-09-14 17:33:26 -0400579 std::unique_ptr<std::thread> ioContextRunner_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400580
Adrien Béraud75754b22023-10-17 09:16:06 -0400581 mutable std::mutex randMtx_;
582 mutable std::mt19937_64 rand_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400583
584 iOSConnectedCallback iOSConnectedCb_ {};
585
Adrien Béraud75754b22023-10-17 09:16:06 -0400586 DeviceInfoSet infos_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400587
588 ChannelRequestCallback channelReqCb_ {};
589 ConnectionReadyCallback connReadyCb_ {};
590 onICERequestCallback iceReqCb_ {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400591 std::atomic_bool isDestroying_ {false};
592};
593
594void
595ConnectionManager::Impl::connectDeviceStartIce(
Adrien Béraud75754b22023-10-17 09:16:06 -0400596 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400597 const std::shared_ptr<dht::crypto::PublicKey>& devicePk,
598 const dht::Value::Id& vid,
599 const std::string& connType,
600 std::function<void(bool)> onConnected)
601{
602 auto deviceId = devicePk->getLongId();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400603 if (!info) {
604 onConnected(false);
605 return;
606 }
607
Adrien Béraud024c46f2024-03-02 23:53:18 -0500608 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400609 auto& ice = info->ice_;
610
611 if (!ice) {
612 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400613 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400614 onConnected(false);
615 return;
616 }
617
618 auto iceAttributes = ice->getLocalAttributes();
619 std::ostringstream icemsg;
620 icemsg << iceAttributes.ufrag << "\n";
621 icemsg << iceAttributes.pwd << "\n";
622 for (const auto& addr : ice->getLocalCandidates(1)) {
623 icemsg << addr << "\n";
624 if (config_->logger)
Sébastien Blinaec46fc2023-07-25 15:43:10 -0400625 config_->logger->debug("[device {}] Added local ICE candidate {}", deviceId, addr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400626 }
627
628 // Prepare connection request as a DHT message
629 PeerConnectionRequest val;
630
631 val.id = vid; /* Random id for the message unicity */
632 val.ice_msg = icemsg.str();
633 val.connType = connType;
634
635 auto value = std::make_shared<dht::Value>(std::move(val));
636 value->user_type = "peer_request";
637
638 // Send connection request through DHT
639 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400640 config_->logger->debug("[device {}] Sending connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400641 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
642 + devicePk->getId().toString()),
643 devicePk,
644 value,
645 [l=config_->logger,deviceId](bool ok) {
646 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -0400647 l->debug("[device {}] Sent connection request. Put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -0400648 deviceId,
649 (ok ? "ok" : "failed"));
650 });
651 // Wait for call to onResponse() operated by DHT
652 if (isDestroying_) {
653 onConnected(true); // This avoid to wait new negotiation when destroying
654 return;
655 }
656
657 info->onConnected_ = std::move(onConnected);
658 info->waitForAnswer_ = std::make_unique<asio::steady_timer>(*config_->ioContext,
659 std::chrono::steady_clock::now()
660 + DHT_MSG_TIMEOUT);
661 info->waitForAnswer_->async_wait(
Adrien Béraud75754b22023-10-17 09:16:06 -0400662 std::bind(&ConnectionManager::Impl::onResponse, this, std::placeholders::_1, info, deviceId, vid));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400663}
664
665void
666ConnectionManager::Impl::onResponse(const asio::error_code& ec,
Adrien Béraud75754b22023-10-17 09:16:06 -0400667 const std::weak_ptr<ConnectionInfo>& winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400668 const DeviceId& deviceId,
669 const dht::Value::Id& vid)
670{
671 if (ec == asio::error::operation_aborted)
672 return;
Adrien Béraud75754b22023-10-17 09:16:06 -0400673 auto info = winfo.lock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400674 if (!info)
675 return;
676
Adrien Béraud024c46f2024-03-02 23:53:18 -0500677 std::unique_lock lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400678 auto& ice = info->ice_;
679 if (isDestroying_) {
680 info->onConnected_(true); // The destructor can wake a pending wait here.
681 return;
682 }
683 if (!info->responseReceived_) {
684 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400685 config_->logger->error("[device {}] no response from DHT to ICE request.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400686 info->onConnected_(false);
687 return;
688 }
689
690 if (!info->ice_) {
691 info->onConnected_(false);
692 return;
693 }
694
695 auto sdp = ice->parseIceCandidates(info->response_.ice_msg);
696
697 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
698 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400699 config_->logger->warn("[device {}] start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400700 info->onConnected_(false);
701 return;
702 }
703 info->onConnected_(true);
704}
705
706bool
707ConnectionManager::Impl::connectDeviceOnNegoDone(
Adrien Béraud75754b22023-10-17 09:16:06 -0400708 const std::weak_ptr<DeviceInfo>& dinfo,
709 const std::shared_ptr<ConnectionInfo>& info,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400710 const DeviceId& deviceId,
711 const std::string& name,
712 const dht::Value::Id& vid,
713 const std::shared_ptr<dht::crypto::Certificate>& cert)
714{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400715 if (!info)
716 return false;
717
Adrien Béraud024c46f2024-03-02 23:53:18 -0500718 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400719 if (info->waitForAnswer_) {
720 // Negotiation is done and connected, go to handshake
721 // and avoid any cancellation at this point.
722 info->waitForAnswer_->cancel();
723 }
724 auto& ice = info->ice_;
725 if (!ice || !ice->isRunning()) {
726 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400727 config_->logger->error("[device {}] No ICE detected or not running", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400728 return false;
729 }
730
731 // Build socket
732 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
733 std::move(ice)),
734 true);
735
736 // Negotiate a TLS session
737 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400738 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 -0400739 info->tls_ = std::make_unique<TlsSocketEndpoint>(std::move(endpoint),
740 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400741 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400742 identity(),
743 dhParams(),
744 *cert);
745
746 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -0400747 [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 -0400748 bool ok) {
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500749 if (auto shared = w.lock())
Andreas Traczykb23278d2023-12-11 16:14:00 -0500750 if (auto info = winfo.lock()) {
751 shared->onTlsNegotiationDone(dinfo.lock(), info, ok, deviceId, vid, name);
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -0500752 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
Andreas Traczykb23278d2023-12-11 16:14:00 -0500753 dht::ThreadPool::io().run([info = std::move(info)] {});
754 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400755 });
756 return true;
757}
758
759void
760ConnectionManager::Impl::connectDevice(const DeviceId& deviceId,
761 const std::string& name,
762 ConnectCallback cb,
763 bool noNewSocket,
764 bool forceNewSocket,
765 const std::string& connType)
766{
767 if (!dht()) {
768 cb(nullptr, deviceId);
769 return;
770 }
771 if (deviceId.toString() == identity().second->getLongId().toString()) {
772 cb(nullptr, deviceId);
773 return;
774 }
775 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400776 [w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400777 deviceId,
778 name,
779 cb = std::move(cb),
780 noNewSocket,
781 forceNewSocket,
782 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
783 if (!cert) {
784 if (auto shared = w.lock())
785 if (shared->config_->logger)
786 shared->config_->logger->error(
787 "No valid certificate found for device {}",
788 deviceId);
789 cb(nullptr, deviceId);
790 return;
791 }
792 if (auto shared = w.lock()) {
793 shared->connectDevice(cert,
794 name,
795 std::move(cb),
796 noNewSocket,
797 forceNewSocket,
798 connType);
799 } else
800 cb(nullptr, deviceId);
801 });
802}
803
804void
Amna0cf544d2023-07-25 14:25:09 -0400805ConnectionManager::Impl::connectDevice(const dht::InfoHash& deviceId,
806 const std::string& name,
807 ConnectCallbackLegacy cb,
808 bool noNewSocket,
809 bool forceNewSocket,
810 const std::string& connType)
811{
812 if (!dht()) {
813 cb(nullptr, deviceId);
814 return;
815 }
816 if (deviceId.toString() == identity().second->getLongId().toString()) {
817 cb(nullptr, deviceId);
818 return;
819 }
820 findCertificate(deviceId,
Adrien Béraud75754b22023-10-17 09:16:06 -0400821 [w = weak_from_this(),
Amna0cf544d2023-07-25 14:25:09 -0400822 deviceId,
823 name,
824 cb = std::move(cb),
825 noNewSocket,
826 forceNewSocket,
827 connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
828 if (!cert) {
829 if (auto shared = w.lock())
830 if (shared->config_->logger)
831 shared->config_->logger->error(
832 "No valid certificate found for device {}",
833 deviceId);
834 cb(nullptr, deviceId);
835 return;
836 }
837 if (auto shared = w.lock()) {
838 shared->connectDevice(cert,
839 name,
Adrien Béraudd78d1ac2023-08-25 10:43:33 -0400840 [cb, deviceId](const std::shared_ptr<ChannelSocket>& sock, const DeviceId& /*did*/){
Amna0cf544d2023-07-25 14:25:09 -0400841 cb(sock, deviceId);
842 },
843 noNewSocket,
844 forceNewSocket,
845 connType);
846 } else
847 cb(nullptr, deviceId);
848 });
849}
850
851void
Adrien Béraud612b55b2023-05-29 10:42:04 -0400852ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
853 const std::string& name,
854 ConnectCallback cb,
855 bool noNewSocket,
856 bool forceNewSocket,
857 const std::string& connType)
858{
859 // Avoid dht operation in a DHT callback to avoid deadlocks
Adrien Béraud75754b22023-10-17 09:16:06 -0400860 dht::ThreadPool::computation().run([w = weak_from_this(),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400861 name = std::move(name),
862 cert = std::move(cert),
863 cb = std::move(cb),
864 noNewSocket,
865 forceNewSocket,
866 connType] {
867 auto devicePk = cert->getSharedPublicKey();
868 auto deviceId = devicePk->getLongId();
869 auto sthis = w.lock();
870 if (!sthis || sthis->isDestroying_) {
871 cb(nullptr, deviceId);
872 return;
873 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400874 auto di = sthis->infos_.createDeviceInfo(deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -0500875 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400876
Adrien Béraud26365c92023-09-23 23:42:43 -0400877 dht::Value::Id vid;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400878 {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500879 std::lock_guard lkr(sthis->randMtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400880 vid = di->newId(sthis->rand_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400881 }
882
Adrien Béraud75754b22023-10-17 09:16:06 -0400883 // Check if already connecting
884 auto isConnectingToDevice = di->isConnecting();
885 // Note: we can be in a state where first
886 // socket is negotiated and first channel is pending
887 // so return only after we checked the info
Adrien Béraudb941e922023-10-16 12:56:14 -0400888 auto& diw = (isConnectingToDevice && !forceNewSocket)
889 ? di->waiting[vid]
890 : di->connecting[vid];
891 diw = PendingCb {name, std::move(cb)};
892
Adrien Béraud612b55b2023-05-29 10:42:04 -0400893 // Check if already negotiated
Adrien Béraud75754b22023-10-17 09:16:06 -0400894 if (auto info = di->getConnectedInfo()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500895 std::unique_lock lkc(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400896 if (auto sock = info->socket_) {
897 info->cbIds_.emplace(vid);
Adrien Béraudb941e922023-10-16 12:56:14 -0400898 diw.requested = true;
Adrien Béraud75754b22023-10-17 09:16:06 -0400899 lkc.unlock();
900 lk.unlock();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400901 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400902 sthis->config_->logger->debug("[device {}] Peer already connected. Add a new channel", deviceId);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -0400903 sthis->sendChannelRequest(di, info, sock, name, vid);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400904 return;
905 }
906 }
907
908 if (isConnectingToDevice && !forceNewSocket) {
909 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400910 sthis->config_->logger->debug("[device {}] Already connecting, wait for ICE negotiation", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400911 return;
912 }
913 if (noNewSocket) {
914 // If no new socket is specified, we don't try to generate a new socket
Adrien Béraud75754b22023-10-17 09:16:06 -0400915 di->executePendingOperations(lk, vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400916 return;
917 }
918
919 // Note: used when the ice negotiation fails to erase
920 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -0400921 auto eraseInfo = [w, diw=std::weak_ptr(di), vid] {
922 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500923 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -0400924 di->info.erase(vid);
925 auto ops = di->extractPendingOperations(vid, nullptr);
926 if (di->empty()) {
927 if (auto shared = w.lock())
928 shared->infos_.removeDeviceInfo(di->deviceId);
929 }
930 lk.unlock();
931 for (const auto& op: ops)
932 op.cb(nullptr, di->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400933 }
934 };
935
936 // If no socket exists, we need to initiate an ICE connection.
937 sthis->getIceOptions([w,
938 deviceId = std::move(deviceId),
939 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400940 diw=std::weak_ptr(di),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400941 name = std::move(name),
942 cert = std::move(cert),
943 vid,
944 connType,
945 eraseInfo](auto&& ice_config) {
946 auto sthis = w.lock();
947 if (!sthis) {
948 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
949 return;
950 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400951 auto info = std::make_shared<ConnectionInfo>();
952 auto winfo = std::weak_ptr(info);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400953 ice_config.tcpEnable = true;
954 ice_config.onInitDone = [w,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400955 devicePk = std::move(devicePk),
956 name = std::move(name),
957 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400958 diw,
959 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400960 vid,
961 connType,
962 eraseInfo](bool ok) {
963 dht::ThreadPool::io().run([w = std::move(w),
964 devicePk = std::move(devicePk),
Adrien Béraud75754b22023-10-17 09:16:06 -0400965 vid,
966 winfo,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400967 eraseInfo,
968 connType, ok] {
969 auto sthis = w.lock();
970 if (!ok && sthis && sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -0400971 sthis->config_->logger->error("[device {}] Cannot initialize ICE session.", devicePk->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400972 if (!sthis || !ok) {
973 eraseInfo();
974 return;
975 }
Adrien Béraud75754b22023-10-17 09:16:06 -0400976 sthis->connectDeviceStartIce(winfo.lock(), devicePk, vid, connType, [=](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400977 if (!ok) {
978 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
979 }
980 });
981 });
982 };
983 ice_config.onNegoDone = [w,
984 deviceId,
985 name,
986 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400987 diw,
988 winfo = std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400989 vid,
990 eraseInfo](bool ok) {
991 dht::ThreadPool::io().run([w = std::move(w),
992 deviceId = std::move(deviceId),
993 name = std::move(name),
994 cert = std::move(cert),
Adrien Béraud75754b22023-10-17 09:16:06 -0400995 diw = std::move(diw),
996 winfo = std::move(winfo),
Adrien Béraud612b55b2023-05-29 10:42:04 -0400997 vid = std::move(vid),
998 eraseInfo = std::move(eraseInfo),
999 ok] {
1000 auto sthis = w.lock();
1001 if (!ok && sthis && sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001002 sthis->config_->logger->error("[device {}] ICE negotiation failed.", deviceId);
Adrien Béraud75754b22023-10-17 09:16:06 -04001003 if (!sthis || !ok || !sthis->connectDeviceOnNegoDone(diw, winfo.lock(), deviceId, name, vid, cert))
Adrien Béraud612b55b2023-05-29 10:42:04 -04001004 eraseInfo();
1005 });
1006 };
1007
Adrien Béraud75754b22023-10-17 09:16:06 -04001008 if (auto di = diw.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001009 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001010 di->info[vid] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001011 }
Adrien Béraud024c46f2024-03-02 23:53:18 -05001012 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001013 ice_config.master = false;
1014 ice_config.streamsCount = 1;
1015 ice_config.compCountPerStream = 1;
Sébastien Blin34086512023-07-25 09:52:14 -04001016 info->ice_ = sthis->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001017 if (!info->ice_) {
1018 if (sthis->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001019 sthis->config_->logger->error("[device {}] Cannot initialize ICE session.", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001020 eraseInfo();
1021 return;
1022 }
1023 // We need to detect any shutdown if the ice session is destroyed before going to the
1024 // TLS session;
1025 info->ice_->setOnShutdown([eraseInfo]() {
1026 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1027 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001028 try {
1029 info->ice_->initIceInstance(ice_config);
1030 } catch (const std::exception& e) {
1031 if (sthis->config_->logger)
1032 sthis->config_->logger->error("{}", e.what());
1033 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1034 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001035 });
1036 });
1037}
1038
1039void
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001040ConnectionManager::Impl::sendChannelRequest(const std::weak_ptr<DeviceInfo>& dinfow,
1041 const std::weak_ptr<ConnectionInfo>& cinfow,
Adrien Béraud75754b22023-10-17 09:16:06 -04001042 const std::shared_ptr<MultiplexedSocket>& sock,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001043 const std::string& name,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001044 const dht::Value::Id& vid)
1045{
1046 auto channelSock = sock->addChannel(name);
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001047 if (!channelSock) {
1048 if (config_->logger)
1049 config_->logger->error("sendChannelRequest failed - cannot create channel");
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001050 if (auto info = dinfow.lock())
Adrien Béraud9a4e98b2023-10-15 12:10:21 -04001051 info->executePendingOperations(vid, nullptr);
1052 return;
1053 }
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001054 channelSock->onShutdown([dinfow, name, vid] {
1055 if (auto info = dinfow.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001056 info->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001057 });
1058 channelSock->onReady(
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001059 [dinfow, cinfow, wSock = std::weak_ptr(channelSock), name, vid](bool accepted) {
1060 if (auto dinfo = dinfow.lock()) {
1061 dinfo->executePendingOperations(vid, accepted ? wSock.lock() : nullptr, accepted);
Sébastien Blinad161572024-01-31 14:14:51 -05001062 // Always lock top-down cinfo->mutex
1063 dht::ThreadPool::io().run([cinfow, vid]() {
1064 if (auto cinfo = cinfow.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001065 std::lock_guard lk(cinfo->mutex_);
Sébastien Blinad161572024-01-31 14:14:51 -05001066 cinfo->cbIds_.erase(vid);
1067 }
1068 });
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001069 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001070 });
1071
1072 ChannelRequest val;
1073 val.name = channelSock->name();
1074 val.state = ChannelRequestState::REQUEST;
1075 val.channel = channelSock->channel();
1076 msgpack::sbuffer buffer(256);
1077 msgpack::pack(buffer, val);
1078
1079 std::error_code ec;
1080 int res = sock->write(CONTROL_CHANNEL,
1081 reinterpret_cast<const uint8_t*>(buffer.data()),
1082 buffer.size(),
1083 ec);
1084 if (res < 0) {
1085 // TODO check if we should handle errors here
1086 if (config_->logger)
Adrien Béraud75754b22023-10-17 09:16:06 -04001087 config_->logger->error("sendChannelRequest failed - error: {}", ec.message());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001088 }
1089}
1090
1091void
Adrien Béraud1addf952023-09-30 17:38:35 -04001092ConnectionManager::Impl::onPeerResponse(PeerConnectionRequest&& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001093{
1094 auto device = req.owner->getLongId();
Adrien Béraud75754b22023-10-17 09:16:06 -04001095 if (auto info = infos_.getInfo(device, req.id)) {
Adrien Béraud23852462023-07-22 01:46:27 -04001096 if (config_->logger)
1097 config_->logger->debug("[device {}] New response received", device);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001098 std::lock_guard lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001099 info->responseReceived_ = true;
1100 info->response_ = std::move(req);
1101 info->waitForAnswer_->expires_at(std::chrono::steady_clock::now());
1102 info->waitForAnswer_->async_wait(std::bind(&ConnectionManager::Impl::onResponse,
1103 this,
1104 std::placeholders::_1,
Adrien Béraud75754b22023-10-17 09:16:06 -04001105 std::weak_ptr(info),
Adrien Béraud612b55b2023-05-29 10:42:04 -04001106 device,
1107 req.id));
1108 } else {
1109 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001110 config_->logger->warn("[device {}] Respond received, but cannot find request", device);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001111 }
1112}
1113
1114void
1115ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1116{
1117 if (!dht())
1118 return;
1119 dht()->listen<PeerConnectionRequest>(
1120 dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()),
Adrien Béraud75754b22023-10-17 09:16:06 -04001121 [w = weak_from_this()](PeerConnectionRequest&& req) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001122 auto shared = w.lock();
1123 if (!shared)
1124 return false;
Adrien Béraud5aec4102024-02-22 14:15:56 -05001125 if (shared->isMessageTreated(req.id)) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001126 // Message already treated. Just ignore
1127 return true;
1128 }
1129 if (req.isAnswer) {
1130 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001131 shared->config_->logger->debug("[device {}] Received request answer", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001132 } else {
1133 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001134 shared->config_->logger->debug("[device {}] Received request", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001135 }
1136 if (req.isAnswer) {
Adrien Béraud1addf952023-09-30 17:38:35 -04001137 shared->onPeerResponse(std::move(req));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001138 } else {
1139 // Async certificate checking
Sébastien Blin34086512023-07-25 09:52:14 -04001140 shared->findCertificate(
Adrien Béraud612b55b2023-05-29 10:42:04 -04001141 req.from,
1142 [w, req = std::move(req)](
1143 const std::shared_ptr<dht::crypto::Certificate>& cert) mutable {
1144 auto shared = w.lock();
1145 if (!shared)
1146 return;
1147 dht::InfoHash peer_h;
1148 if (foundPeerDevice(cert, peer_h, shared->config_->logger)) {
1149#if TARGET_OS_IOS
1150 if (shared->iOSConnectedCb_(req.connType, peer_h))
1151 return;
1152#endif
1153 shared->onDhtPeerRequest(req, cert);
1154 } else {
1155 if (shared->config_->logger)
1156 shared->config_->logger->warn(
Adrien Béraud23852462023-07-22 01:46:27 -04001157 "[device {}] Received request from untrusted peer",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001158 req.owner->getLongId());
1159 }
1160 });
1161 }
1162
1163 return true;
1164 },
1165 dht::Value::UserTypeFilter("peer_request"));
1166}
1167
1168void
Adrien Béraud75754b22023-10-17 09:16:06 -04001169ConnectionManager::Impl::onTlsNegotiationDone(const std::shared_ptr<DeviceInfo>& dinfo,
1170 const std::shared_ptr<ConnectionInfo>& info,
1171 bool ok,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001172 const DeviceId& deviceId,
1173 const dht::Value::Id& vid,
1174 const std::string& name)
1175{
1176 if (isDestroying_)
1177 return;
1178 // Note: only handle pendingCallbacks here for TLS initied by connectDevice()
1179 // Note: if not initied by connectDevice() the channel name will be empty (because no channel
1180 // asked yet)
1181 auto isDhtRequest = name.empty();
1182 if (!ok) {
1183 if (isDhtRequest) {
1184 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001185 config_->logger->error("[device {}] TLS connection failure - Initied by DHT request. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001186 deviceId,
1187 name,
1188 vid);
1189 if (connReadyCb_)
1190 connReadyCb_(deviceId, "", nullptr);
1191 } else {
1192 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001193 config_->logger->error("[device {}] TLS connection failure - Initied by connectDevice. channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001194 deviceId,
1195 name,
1196 vid);
Adrien Béraud75754b22023-10-17 09:16:06 -04001197 dinfo->executePendingOperations(vid, nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001198 }
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001199
Adrien Béraud024c46f2024-03-02 23:53:18 -05001200 std::unique_lock lk(dinfo->mtx_);
Sébastien Blin3cf0acc2023-10-23 09:45:32 -04001201 dinfo->info.erase(vid);
1202
1203 if (dinfo->empty()) {
1204 infos_.removeDeviceInfo(dinfo->deviceId);
1205 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001206 } else {
1207 // The socket is ready, store it
1208 if (isDhtRequest) {
1209 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001210 config_->logger->debug("[device {}] Connection is ready - Initied by DHT request. Vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001211 deviceId,
1212 vid);
1213 } else {
1214 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001215 config_->logger->debug("[device {}] Connection is ready - Initied by connectDevice(). channel: {} - vid: {}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001216 deviceId,
1217 name,
1218 vid);
1219 }
1220
Adrien Béraud75754b22023-10-17 09:16:06 -04001221 // Note: do not remove pending there it's done in sendChannelRequest
Adrien Béraud024c46f2024-03-02 23:53:18 -05001222 std::unique_lock lk2 {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001223 auto pendingIds = dinfo->requestPendingOps();
Adrien Béraud75754b22023-10-17 09:16:06 -04001224 lk2.unlock();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001225 std::unique_lock lk {info->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001226 addNewMultiplexedSocket(dinfo, deviceId, vid, info);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001227 // Finally, open the channel and launch pending callbacks
Adrien Béraud75754b22023-10-17 09:16:06 -04001228 lk.unlock();
1229 for (const auto& [id, name]: pendingIds) {
1230 if (config_->logger)
1231 config_->logger->debug("[device {}] Send request on TLS socket for channel {}",
1232 deviceId, name);
Adrien Bérauda9ef2a52023-11-05 00:47:24 -04001233 sendChannelRequest(dinfo, info, info->socket_, name, id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001234 }
1235 }
1236}
1237
1238void
1239ConnectionManager::Impl::answerTo(IceTransport& ice,
1240 const dht::Value::Id& id,
1241 const std::shared_ptr<dht::crypto::PublicKey>& from)
1242{
1243 // NOTE: This is a shortest version of a real SDP message to save some bits
1244 auto iceAttributes = ice.getLocalAttributes();
1245 std::ostringstream icemsg;
1246 icemsg << iceAttributes.ufrag << "\n";
1247 icemsg << iceAttributes.pwd << "\n";
1248 for (const auto& addr : ice.getLocalCandidates(1)) {
1249 icemsg << addr << "\n";
1250 }
1251
1252 // Send PeerConnection response
1253 PeerConnectionRequest val;
1254 val.id = id;
1255 val.ice_msg = icemsg.str();
1256 val.isAnswer = true;
1257 auto value = std::make_shared<dht::Value>(std::move(val));
1258 value->user_type = "peer_request";
1259
1260 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001261 config_->logger->debug("[device {}] Connection accepted, DHT reply", from->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001262 dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
1263 + from->getId().toString()),
1264 from,
1265 value,
1266 [from,l=config_->logger](bool ok) {
1267 if (l)
Adrien Béraud23852462023-07-22 01:46:27 -04001268 l->debug("[device {}] Answer to connection request: put encrypted {:s}",
Adrien Béraud612b55b2023-05-29 10:42:04 -04001269 from->getLongId(),
1270 (ok ? "ok" : "failed"));
1271 });
1272}
1273
1274bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001275ConnectionManager::Impl::onRequestStartIce(const std::shared_ptr<ConnectionInfo>& info, const PeerConnectionRequest& req)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001276{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001277 if (!info)
1278 return false;
1279
Adrien Béraud75754b22023-10-17 09:16:06 -04001280 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001281 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001282 auto& ice = info->ice_;
1283 if (!ice) {
1284 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001285 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001286 if (connReadyCb_)
1287 connReadyCb_(deviceId, "", nullptr);
1288 return false;
1289 }
1290
1291 auto sdp = ice->parseIceCandidates(req.ice_msg);
1292 answerTo(*ice, req.id, req.owner);
1293 if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
1294 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001295 config_->logger->error("[device {}] Start ICE failed", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001296 ice = nullptr;
1297 if (connReadyCb_)
1298 connReadyCb_(deviceId, "", nullptr);
1299 return false;
1300 }
1301 return true;
1302}
1303
1304bool
Adrien Béraud75754b22023-10-17 09:16:06 -04001305ConnectionManager::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 -04001306{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001307 if (!info)
1308 return false;
1309
Adrien Béraud75754b22023-10-17 09:16:06 -04001310 auto deviceId = req.owner->getLongId();
Adrien Béraud024c46f2024-03-02 23:53:18 -05001311 std::unique_lock lk {info->mutex_};
Adrien Béraud612b55b2023-05-29 10:42:04 -04001312 auto& ice = info->ice_;
1313 if (!ice) {
1314 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001315 config_->logger->error("[device {}] No ICE detected", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001316 return false;
1317 }
1318
1319 // Build socket
1320 auto endpoint = std::make_unique<IceSocketEndpoint>(std::shared_ptr<IceTransport>(
1321 std::move(ice)),
1322 false);
1323
1324 // init TLS session
1325 auto ph = req.from;
1326 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001327 config_->logger->debug("[device {}] Start TLS session - Initied by DHT request. vid: {}",
1328 deviceId,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001329 req.id);
1330 info->tls_ = std::make_unique<TlsSocketEndpoint>(
1331 std::move(endpoint),
1332 certStore(),
Adrien Béraud3f93ddf2023-07-21 14:46:22 -04001333 config_->ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001334 identity(),
1335 dhParams(),
Adrien Béraud75754b22023-10-17 09:16:06 -04001336 [ph, deviceId, w=weak_from_this(), l=config_->logger](const dht::crypto::Certificate& cert) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001337 auto shared = w.lock();
1338 if (!shared)
1339 return false;
Adrien Béraud9efbd442023-08-27 12:38:07 -04001340 if (cert.getPublicKey().getId() != ph
1341 || deviceId != cert.getPublicKey().getLongId()) {
1342 if (l) l->warn("[device {}] TLS certificate with ID {} doesn't match the DHT request.",
1343 deviceId,
1344 cert.getPublicKey().getLongId());
1345 return false;
1346 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001347 auto crt = shared->certStore().getCertificate(cert.getLongId().toString());
1348 if (!crt)
1349 return false;
1350 return crt->getPacked() == cert.getPacked();
1351 });
1352
1353 info->tls_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001354 [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 -04001355 if (auto shared = w.lock())
Andreas Traczyk8b6e99f2024-01-04 17:12:55 -05001356 if (auto info = winfo.lock()) {
1357 shared->onTlsNegotiationDone(dinfo.lock(), winfo.lock(), ok, deviceId, vid);
1358 // Make another reference to info to avoid destruction (could lead to a deadlock/crash).
1359 dht::ThreadPool::io().run([info = std::move(info)] {});
1360 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001361 });
1362 return true;
1363}
1364
1365void
1366ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
1367 const std::shared_ptr<dht::crypto::Certificate>& /*cert*/)
1368{
1369 auto deviceId = req.owner->getLongId();
1370 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001371 config_->logger->debug("[device {}] New connection request", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001372 if (!iceReqCb_ || !iceReqCb_(deviceId)) {
1373 if (config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001374 config_->logger->debug("[device {}] Refusing connection", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001375 return;
1376 }
1377
1378 // Because the connection is accepted, create an ICE socket.
Adrien Béraud75754b22023-10-17 09:16:06 -04001379 getIceOptions([w = weak_from_this(), req, deviceId](auto&& ice_config) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001380 auto shared = w.lock();
1381 if (!shared)
1382 return;
Adrien Béraud75754b22023-10-17 09:16:06 -04001383
1384 auto di = shared->infos_.createDeviceInfo(deviceId);
1385 auto info = std::make_shared<ConnectionInfo>();
1386 auto wdi = std::weak_ptr(di);
1387 auto winfo = std::weak_ptr(info);
1388
Adrien Béraud612b55b2023-05-29 10:42:04 -04001389 // Note: used when the ice negotiation fails to erase
1390 // all stored structures.
Adrien Béraud75754b22023-10-17 09:16:06 -04001391 auto eraseInfo = [w, wdi, id = req.id] {
1392 auto shared = w.lock();
1393 if (auto di = wdi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001394 std::unique_lock lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001395 di->info.erase(id);
1396 auto ops = di->extractPendingOperations(id, nullptr);
1397 if (di->empty()) {
1398 if (shared)
1399 shared->infos_.removeDeviceInfo(di->deviceId);
1400 }
1401 lk.unlock();
1402 for (const auto& op: ops)
1403 op.cb(nullptr, di->deviceId);
1404 if (shared && shared->connReadyCb_)
1405 shared->connReadyCb_(di->deviceId, "", nullptr);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001406 }
1407 };
1408
Adrien Béraud75754b22023-10-17 09:16:06 -04001409 ice_config.master = true;
1410 ice_config.streamsCount = 1;
1411 ice_config.compCountPerStream = 1; // TCP
Adrien Béraud612b55b2023-05-29 10:42:04 -04001412 ice_config.tcpEnable = true;
Adrien Béraud75754b22023-10-17 09:16:06 -04001413 ice_config.onInitDone = [w, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001414 auto shared = w.lock();
1415 if (!shared)
1416 return;
1417 if (!ok) {
1418 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001419 shared->config_->logger->error("[device {}] Cannot initialize ICE session.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001420 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1421 return;
1422 }
1423
1424 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001425 [w = std::move(w), winfo = std::move(winfo), req = std::move(req), eraseInfo = std::move(eraseInfo)] {
1426 if (auto shared = w.lock()) {
1427 if (!shared->onRequestStartIce(winfo.lock(), req))
1428 eraseInfo();
1429 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001430 });
1431 };
1432
Adrien Béraud75754b22023-10-17 09:16:06 -04001433 ice_config.onNegoDone = [w, wdi, winfo, req, eraseInfo](bool ok) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001434 auto shared = w.lock();
1435 if (!shared)
1436 return;
1437 if (!ok) {
1438 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001439 shared->config_->logger->error("[device {}] ICE negotiation failed.", req.owner->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001440 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1441 return;
1442 }
1443
1444 dht::ThreadPool::io().run(
Adrien Béraud75754b22023-10-17 09:16:06 -04001445 [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 -04001446 if (auto shared = w.lock())
Adrien Béraud75754b22023-10-17 09:16:06 -04001447 if (!shared->onRequestOnNegoDone(wdi.lock(), winfo.lock(), req))
Adrien Béraud612b55b2023-05-29 10:42:04 -04001448 eraseInfo();
1449 });
1450 };
1451
1452 // Negotiate a new ICE socket
Adrien Béraud612b55b2023-05-29 10:42:04 -04001453 {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001454 std::lock_guard lk(di->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001455 di->info[req.id] = info;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001456 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001457
Adrien Béraud612b55b2023-05-29 10:42:04 -04001458 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001459 shared->config_->logger->debug("[device {}] Accepting connection", deviceId);
Adrien Béraud024c46f2024-03-02 23:53:18 -05001460 std::unique_lock lk {info->mutex_};
Sébastien Blin34086512023-07-25 09:52:14 -04001461 info->ice_ = shared->config_->factory->createUTransport("");
Adrien Béraud612b55b2023-05-29 10:42:04 -04001462 if (not info->ice_) {
1463 if (shared->config_->logger)
Adrien Béraud23852462023-07-22 01:46:27 -04001464 shared->config_->logger->error("[device {}] Cannot initialize ICE session", deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001465 eraseInfo();
1466 return;
1467 }
1468 // We need to detect any shutdown if the ice session is destroyed before going to the TLS session;
1469 info->ice_->setOnShutdown([eraseInfo]() {
1470 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1471 });
Adrien Béraud4cda2d72023-06-01 15:44:43 -04001472 try {
1473 info->ice_->initIceInstance(ice_config);
1474 } catch (const std::exception& e) {
1475 if (shared->config_->logger)
1476 shared->config_->logger->error("{}", e.what());
1477 dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
1478 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001479 });
1480}
1481
1482void
Adrien Béraud75754b22023-10-17 09:16:06 -04001483ConnectionManager::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 -04001484{
Adrien Béraud75754b22023-10-17 09:16:06 -04001485 info->socket_ = std::make_shared<MultiplexedSocket>(config_->ioContext, deviceId, std::move(info->tls_), config_->logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001486 info->socket_->setOnReady(
Adrien Béraud75754b22023-10-17 09:16:06 -04001487 [w = weak_from_this()](const DeviceId& deviceId, const std::shared_ptr<ChannelSocket>& socket) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001488 if (auto sthis = w.lock())
1489 if (sthis->connReadyCb_)
1490 sthis->connReadyCb_(deviceId, socket->name(), socket);
1491 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001492 info->socket_->setOnRequest([w = weak_from_this()](const std::shared_ptr<dht::crypto::Certificate>& peer,
Adrien Béraud612b55b2023-05-29 10:42:04 -04001493 const uint16_t&,
1494 const std::string& name) {
1495 if (auto sthis = w.lock())
1496 if (sthis->channelReqCb_)
1497 return sthis->channelReqCb_(peer, name);
1498 return false;
1499 });
Adrien Béraud75754b22023-10-17 09:16:06 -04001500 info->socket_->onShutdown([dinfo, wi=std::weak_ptr(info), vid]() {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001501 // Cancel current outgoing connections
Adrien Béraud75754b22023-10-17 09:16:06 -04001502 dht::ThreadPool::io().run([dinfo, wi, vid] {
1503 std::set<dht::Value::Id> ids;
1504 if (auto info = wi.lock()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001505 std::lock_guard lk(info->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001506 if (info->socket_) {
1507 ids = std::move(info->cbIds_);
1508 info->socket_->shutdown();
1509 }
1510 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001511 if (auto deviceInfo = dinfo.lock()) {
1512 std::shared_ptr<ConnectionInfo> info;
1513 std::vector<PendingCb> ops;
Adrien Béraud024c46f2024-03-02 23:53:18 -05001514 std::unique_lock lk(deviceInfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001515 auto it = deviceInfo->info.find(vid);
1516 if (it != deviceInfo->info.end()) {
1517 info = std::move(it->second);
1518 deviceInfo->info.erase(it);
1519 }
1520 for (const auto& cbId : ids) {
1521 auto po = deviceInfo->extractPendingOperations(cbId, nullptr);
1522 ops.insert(ops.end(), po.begin(), po.end());
1523 }
1524 lk.unlock();
1525 for (auto& op : ops)
1526 op.cb(nullptr, deviceInfo->deviceId);
1527 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001528 });
1529 });
1530}
1531
1532const std::shared_future<tls::DhParams>
1533ConnectionManager::Impl::dhParams() const
1534{
1535 return dht::ThreadPool::computation().get<tls::DhParams>(
Adrien Béraud2a4e73d2023-08-27 12:53:55 -04001536 std::bind(tls::DhParams::loadDhParams, config_->cachePath / "dhParams"));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001537}
1538
Adrien Béraud612b55b2023-05-29 10:42:04 -04001539bool
Adrien Béraud5aec4102024-02-22 14:15:56 -05001540ConnectionManager::Impl::isMessageTreated(dht::Value::Id id)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001541{
Adrien Béraud024c46f2024-03-02 23:53:18 -05001542 std::lock_guard lock(messageMutex_);
Adrien Béraud28e2ca52024-02-23 14:19:59 -05001543 return !treatedMessages_.add(id);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001544}
1545
1546/**
1547 * returns whether or not UPnP is enabled and active_
1548 * ie: if it is able to make port mappings
1549 */
1550bool
1551ConnectionManager::Impl::getUPnPActive() const
1552{
1553 return config_->getUPnPActive();
1554}
1555
1556IpAddr
1557ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
1558{
1559 if (family == AF_INET)
1560 return publishedIp_[0];
1561 if (family == AF_INET6)
1562 return publishedIp_[1];
1563
1564 assert(family == AF_UNSPEC);
1565
1566 // If family is not set, prefere IPv4 if available. It's more
1567 // likely to succeed behind NAT.
1568 if (publishedIp_[0])
1569 return publishedIp_[0];
1570 if (publishedIp_[1])
1571 return publishedIp_[1];
1572 return {};
1573}
1574
1575void
1576ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr)
1577{
1578 if (ip_addr.getFamily() == AF_INET) {
1579 publishedIp_[0] = ip_addr;
1580 } else {
1581 publishedIp_[1] = ip_addr;
1582 }
1583}
1584
1585void
1586ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb)
1587{
Adrien Béraud75754b22023-10-17 09:16:06 -04001588 dht()->getPublicAddress([w=weak_from_this(), cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
Sébastien Blinb6504372023-10-12 10:35:35 -04001589 auto shared = w.lock();
1590 if (!shared)
1591 return;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001592 bool hasIpv4 {false}, hasIpv6 {false};
1593 for (auto& result : results) {
1594 auto family = result.getFamily();
1595 if (family == AF_INET) {
1596 if (not hasIpv4) {
1597 hasIpv4 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001598 if (shared->config_->logger)
1599 shared->config_->logger->debug("Store DHT public IPv4 address: {}", result);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001600 //JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str());
Sébastien Blinb6504372023-10-12 10:35:35 -04001601 shared->setPublishedAddress(*result.get());
1602 if (shared->config_->upnpCtrl) {
1603 shared->config_->upnpCtrl->setPublicAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001604 }
1605 }
1606 } else if (family == AF_INET6) {
1607 if (not hasIpv6) {
1608 hasIpv6 = true;
Sébastien Blinb6504372023-10-12 10:35:35 -04001609 if (shared->config_->logger)
1610 shared->config_->logger->debug("Store DHT public IPv6 address: {}", result);
1611 shared->setPublishedAddress(*result.get());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001612 }
1613 }
1614 if (hasIpv4 and hasIpv6)
1615 break;
1616 }
1617 if (cb)
1618 cb();
1619 });
1620}
1621
1622void
1623ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1624{
1625 storeActiveIpAddress([this, cb = std::move(cb)] {
1626 IceTransportOptions opts = ConnectionManager::Impl::getIceOptions();
1627 auto publishedAddr = getPublishedIpAddress();
1628
1629 if (publishedAddr) {
1630 auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
1631 publishedAddr.getFamily());
1632 if (interfaceAddr) {
1633 opts.accountLocalAddr = interfaceAddr;
1634 opts.accountPublicAddr = publishedAddr;
1635 }
1636 }
1637 if (cb)
1638 cb(std::move(opts));
1639 });
1640}
1641
1642IceTransportOptions
1643ConnectionManager::Impl::getIceOptions() const noexcept
1644{
1645 IceTransportOptions opts;
Sébastien Blin34086512023-07-25 09:52:14 -04001646 opts.factory = config_->factory;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001647 opts.upnpEnable = getUPnPActive();
Adrien Béraud7b869d92023-08-21 09:02:35 -04001648 opts.upnpContext = config_->upnpCtrl ? config_->upnpCtrl->upnpContext() : nullptr;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001649
1650 if (config_->stunEnabled)
1651 opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer));
1652 if (config_->turnEnabled) {
Sébastien Blin84bf4182023-07-21 14:18:39 -04001653 if (config_->turnCache) {
1654 auto turnAddr = config_->turnCache->getResolvedTurn();
1655 if (turnAddr != std::nullopt) {
1656 opts.turnServers.emplace_back(TurnServerInfo()
1657 .setUri(turnAddr->toString())
1658 .setUsername(config_->turnServerUserName)
1659 .setPassword(config_->turnServerPwd)
1660 .setRealm(config_->turnServerRealm));
1661 }
1662 } else {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001663 opts.turnServers.emplace_back(TurnServerInfo()
Sébastien Blin84bf4182023-07-21 14:18:39 -04001664 .setUri(config_->turnServer)
1665 .setUsername(config_->turnServerUserName)
1666 .setPassword(config_->turnServerPwd)
1667 .setRealm(config_->turnServerRealm));
Adrien Béraud612b55b2023-05-29 10:42:04 -04001668 }
1669 // NOTE: first test with ipv6 turn was not concluant and resulted in multiple
1670 // co issues. So this needs some debug. for now just disable
1671 // if (cacheTurnV6 && *cacheTurnV6) {
1672 // opts.turnServers.emplace_back(TurnServerInfo()
1673 // .setUri(cacheTurnV6->toString(true))
1674 // .setUsername(turnServerUserName_)
1675 // .setPassword(turnServerPwd_)
1676 // .setRealm(turnServerRealm_));
1677 //}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001678 }
1679 return opts;
1680}
1681
1682bool
1683ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
1684 dht::InfoHash& account_id,
1685 const std::shared_ptr<Logger>& logger)
1686{
1687 if (not crt)
1688 return false;
1689
1690 auto top_issuer = crt;
1691 while (top_issuer->issuer)
1692 top_issuer = top_issuer->issuer;
1693
1694 // Device certificate can't be self-signed
Adrien Béraudc631a832023-07-26 22:19:00 -04001695 if (top_issuer == crt) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001696 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001697 logger->warn("Found invalid (self-signed) peer device: {}", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001698 return false;
Adrien Béraudc631a832023-07-26 22:19:00 -04001699 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001700
1701 // Check peer certificate chain
1702 // Trust store with top issuer as the only CA
1703 dht::crypto::TrustList peer_trust;
1704 peer_trust.add(*top_issuer);
1705 if (not peer_trust.verify(*crt)) {
1706 if (logger)
1707 logger->warn("Found invalid peer device: {}", crt->getLongId());
1708 return false;
1709 }
1710
1711 // Check cached OCSP response
1712 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
1713 if (logger)
Adrien Béraud8b831a82023-07-21 14:13:06 -04001714 logger->error("Certificate {} is disabled by cached OCSP response", crt->getLongId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001715 return false;
1716 }
1717
Adrien Béraudc631a832023-07-26 22:19:00 -04001718 account_id = crt->issuer->getId();
1719 if (logger)
1720 logger->warn("Found peer device: {} account:{} CA:{}",
1721 crt->getLongId(),
1722 account_id,
1723 top_issuer->getId());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001724 return true;
1725}
1726
1727bool
1728ConnectionManager::Impl::findCertificate(
1729 const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1730{
1731 if (auto cert = certStore().getCertificate(id.toString())) {
1732 if (cb)
1733 cb(cert);
1734 } else if (cb)
1735 cb(nullptr);
1736 return true;
1737}
1738
Sébastien Blin34086512023-07-25 09:52:14 -04001739bool
1740ConnectionManager::Impl::findCertificate(const dht::InfoHash& h,
1741 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
1742{
1743 if (auto cert = certStore().getCertificate(h.toString())) {
1744 if (cb)
1745 cb(cert);
1746 } else {
1747 dht()->findCertificate(h,
1748 [cb = std::move(cb), this](
1749 const std::shared_ptr<dht::crypto::Certificate>& crt) {
1750 if (crt)
1751 certStore().pinCertificate(crt);
1752 if (cb)
1753 cb(crt);
1754 });
1755 }
1756 return true;
1757}
1758
Amna81221ad2023-09-14 17:33:26 -04001759std::shared_ptr<ConnectionManager::Config>
1760buildDefaultConfig(dht::crypto::Identity id){
1761 auto conf = std::make_shared<ConnectionManager::Config>();
1762 conf->id = std::move(id);
1763 return conf;
1764}
1765
Adrien Béraud612b55b2023-05-29 10:42:04 -04001766ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_)
1767 : pimpl_ {std::make_shared<Impl>(config_)}
1768{}
1769
Amna81221ad2023-09-14 17:33:26 -04001770ConnectionManager::ConnectionManager(dht::crypto::Identity id)
1771 : ConnectionManager {buildDefaultConfig(id)}
1772{}
1773
Adrien Béraud612b55b2023-05-29 10:42:04 -04001774ConnectionManager::~ConnectionManager()
1775{
1776 if (pimpl_)
1777 pimpl_->shutdown();
1778}
1779
1780void
1781ConnectionManager::connectDevice(const DeviceId& deviceId,
1782 const std::string& name,
1783 ConnectCallback cb,
1784 bool noNewSocket,
1785 bool forceNewSocket,
1786 const std::string& connType)
1787{
1788 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1789}
1790
1791void
Amna0cf544d2023-07-25 14:25:09 -04001792ConnectionManager::connectDevice(const dht::InfoHash& deviceId,
1793 const std::string& name,
1794 ConnectCallbackLegacy cb,
1795 bool noNewSocket,
1796 bool forceNewSocket,
1797 const std::string& connType)
1798{
1799 pimpl_->connectDevice(deviceId, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1800}
1801
1802
1803void
Adrien Béraud612b55b2023-05-29 10:42:04 -04001804ConnectionManager::connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
1805 const std::string& name,
1806 ConnectCallback cb,
1807 bool noNewSocket,
1808 bool forceNewSocket,
1809 const std::string& connType)
1810{
1811 pimpl_->connectDevice(cert, name, std::move(cb), noNewSocket, forceNewSocket, connType);
1812}
1813
1814bool
1815ConnectionManager::isConnecting(const DeviceId& deviceId, const std::string& name) const
1816{
Adrien Béraud75754b22023-10-17 09:16:06 -04001817 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001818 std::unique_lock lk {dinfo->mtx_};
Adrien Béraudb941e922023-10-16 12:56:14 -04001819 return dinfo->isConnecting(name);
Adrien Béraud75754b22023-10-17 09:16:06 -04001820 }
1821 return false;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001822}
1823
Sébastien Blind0c92c72023-12-07 15:27:51 -05001824bool
1825ConnectionManager::isConnected(const DeviceId& deviceId) const
1826{
1827 if (auto dinfo = pimpl_->infos_.getDeviceInfo(deviceId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001828 std::unique_lock lk {dinfo->mtx_};
Sébastien Blind0c92c72023-12-07 15:27:51 -05001829 return dinfo->getConnectedInfo() != nullptr;
1830 }
1831 return false;
1832}
1833
Adrien Béraud612b55b2023-05-29 10:42:04 -04001834void
1835ConnectionManager::closeConnectionsWith(const std::string& peerUri)
1836{
Adrien Béraud75754b22023-10-17 09:16:06 -04001837 std::vector<std::shared_ptr<DeviceInfo>> dInfos;
1838 for (const auto& dinfo: pimpl_->infos_.getDeviceInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001839 std::unique_lock lk(dinfo->mtx_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001840 bool isPeer = false;
1841 for (auto const& [id, cinfo]: dinfo->info) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001842 std::lock_guard lkv {cinfo->mutex_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001843 auto tls = cinfo->tls_ ? cinfo->tls_.get() : (cinfo->socket_ ? cinfo->socket_->endpoint() : nullptr);
Adrien Béraudafa8e282023-09-24 12:53:20 -04001844 auto cert = tls ? tls->peerCertificate() : nullptr;
1845 if (not cert)
Adrien Béraud75754b22023-10-17 09:16:06 -04001846 cert = pimpl_->certStore().getCertificate(dinfo->deviceId.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001847 if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001848 isPeer = true;
1849 break;
Adrien Béraud612b55b2023-05-29 10:42:04 -04001850 }
1851 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001852 lk.unlock();
1853 if (isPeer) {
1854 dInfos.emplace_back(std::move(dinfo));
1855 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001856 }
1857 // Stop connections to all peers devices
Adrien Béraud75754b22023-10-17 09:16:06 -04001858 for (const auto& dinfo : dInfos) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001859 std::unique_lock lk {dinfo->mtx_};
Adrien Béraud75754b22023-10-17 09:16:06 -04001860 auto unused = dinfo->extractUnusedConnections();
1861 auto pending = dinfo->extractPendingOperations(0, nullptr);
1862 pimpl_->infos_.removeDeviceInfo(dinfo->deviceId);
1863 lk.unlock();
1864 for (auto& op : unused)
1865 op->shutdown();
1866 for (auto& op : pending)
1867 op.cb(nullptr, dinfo->deviceId);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001868 }
1869}
1870
1871void
1872ConnectionManager::onDhtConnected(const dht::crypto::PublicKey& devicePk)
1873{
1874 pimpl_->onDhtConnected(devicePk);
1875}
1876
1877void
1878ConnectionManager::onICERequest(onICERequestCallback&& cb)
1879{
1880 pimpl_->iceReqCb_ = std::move(cb);
1881}
1882
1883void
1884ConnectionManager::onChannelRequest(ChannelRequestCallback&& cb)
1885{
1886 pimpl_->channelReqCb_ = std::move(cb);
1887}
1888
1889void
1890ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb)
1891{
1892 pimpl_->connReadyCb_ = std::move(cb);
1893}
1894
1895void
1896ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb)
1897{
1898 pimpl_->iOSConnectedCb_ = std::move(cb);
1899}
1900
1901std::size_t
1902ConnectionManager::activeSockets() const
1903{
Adrien Béraud75754b22023-10-17 09:16:06 -04001904 return pimpl_->infos_.getConnectedInfos().size();
Adrien Béraud612b55b2023-05-29 10:42:04 -04001905}
1906
1907void
1908ConnectionManager::monitor() const
1909{
Adrien Béraud612b55b2023-05-29 10:42:04 -04001910 auto logger = pimpl_->config_->logger;
1911 if (!logger)
1912 return;
1913 logger->debug("ConnectionManager current status:");
Adrien Béraud75754b22023-10-17 09:16:06 -04001914 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001915 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001916 if (ci->socket_)
1917 ci->socket_->monitor();
1918 }
1919 logger->debug("ConnectionManager end status.");
1920}
1921
1922void
1923ConnectionManager::connectivityChanged()
1924{
Adrien Béraud75754b22023-10-17 09:16:06 -04001925 for (const auto& ci : pimpl_->infos_.getConnectedInfos()) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001926 std::lock_guard lk(ci->mutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001927 if (ci->socket_)
Adrien Béraud51a54712023-10-17 21:24:30 -04001928 dht::ThreadPool::io().run([s = ci->socket_] { s->sendBeacon(); });
Adrien Béraud612b55b2023-05-29 10:42:04 -04001929 }
1930}
1931
1932void
1933ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
1934{
1935 return pimpl_->getIceOptions(std::move(cb));
1936}
1937
1938IceTransportOptions
1939ConnectionManager::getIceOptions() const noexcept
1940{
1941 return pimpl_->getIceOptions();
1942}
1943
1944IpAddr
1945ConnectionManager::getPublishedIpAddress(uint16_t family) const
1946{
1947 return pimpl_->getPublishedIpAddress(family);
1948}
1949
1950void
1951ConnectionManager::setPublishedAddress(const IpAddr& ip_addr)
1952{
1953 return pimpl_->setPublishedAddress(ip_addr);
1954}
1955
1956void
1957ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb)
1958{
1959 return pimpl_->storeActiveIpAddress(std::move(cb));
1960}
1961
1962std::shared_ptr<ConnectionManager::Config>
1963ConnectionManager::getConfig()
1964{
1965 return pimpl_->config_;
1966}
1967
Amna31791e52023-08-03 12:40:57 -04001968std::vector<std::map<std::string, std::string>>
1969ConnectionManager::getConnectionList(const DeviceId& device) const
1970{
1971 std::vector<std::map<std::string, std::string>> connectionsList;
Amna31791e52023-08-03 12:40:57 -04001972 if (device) {
Adrien Béraud75754b22023-10-17 09:16:06 -04001973 if (auto deviceInfo = pimpl_->infos_.getDeviceInfo(device)) {
1974 connectionsList = deviceInfo->getConnectionList(pimpl_->certStore());
Amna31791e52023-08-03 12:40:57 -04001975 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001976 } else {
1977 for (const auto& deviceInfo : pimpl_->infos_.getDeviceInfos()) {
1978 auto cl = deviceInfo->getConnectionList(pimpl_->certStore());
1979 connectionsList.insert(connectionsList.end(), std::make_move_iterator(cl.begin()), std::make_move_iterator(cl.end()));
Amna31791e52023-08-03 12:40:57 -04001980 }
1981 }
1982 return connectionsList;
1983}
1984
1985std::vector<std::map<std::string, std::string>>
1986ConnectionManager::getChannelList(const std::string& connectionId) const
1987{
Adrien Béraud75754b22023-10-17 09:16:06 -04001988 auto [deviceId, valueId] = parseCallbackId(connectionId);
1989 if (auto info = pimpl_->infos_.getInfo(deviceId, valueId)) {
Adrien Béraud024c46f2024-03-02 23:53:18 -05001990 std::lock_guard lk(info->mutex_);
Adrien Béraud75754b22023-10-17 09:16:06 -04001991 if (info->socket_)
1992 return info->socket_->getChannelList();
Amna31791e52023-08-03 12:40:57 -04001993 }
Adrien Béraud75754b22023-10-17 09:16:06 -04001994 return {};
Amna31791e52023-08-03 12:40:57 -04001995}
1996
Sébastien Blin464bdff2023-07-19 08:02:53 -04001997} // namespace dhtnet