My Project 3.5.5
C++ Distributed Hash Table
Loading...
Searching...
No Matches
dhtrunner.h
1/*
2 * Copyright (C) 2014-2025 Savoir-faire Linux Inc.
3 * Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
4 * Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
5 * Sébastien Blin <sebastien.blin@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include "def.h"
24#include "infohash.h"
25#include "value.h"
26#include "callbacks.h"
27#include "sockaddr.h"
28#include "logger.h"
29#include "network_utils.h"
30#include "node_export.h"
31
32#include <thread>
33#include <mutex>
34#include <atomic>
35#include <condition_variable>
36#include <future>
37#include <exception>
38#include <queue>
39#include <chrono>
40
41namespace dht {
42
43struct Node;
44class SecureDht;
45class PeerDiscovery;
46struct SecureDhtConfig;
47
54class OPENDHT_PUBLIC DhtRunner {
55
56public:
57 using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
58
59 struct Config {
60 SecureDhtConfig dht_config {};
61 bool threaded {true};
62 std::string proxy_server {};
63 std::string push_node_id {};
64 std::string push_token {};
65 std::string push_topic {};
66 std::string push_platform {};
67 std::string proxy_user_agent {};
68 bool peer_discovery {false};
69 bool peer_publish {false};
70 std::shared_ptr<dht::crypto::Certificate> server_ca;
71 dht::crypto::Identity client_identity;
72 SockAddr bind4 {}, bind6 {};
73 };
74
75 struct Context {
76 std::shared_ptr<Logger> logger {};
77 std::unique_ptr<net::DatagramSocket> sock;
78 std::shared_ptr<PeerDiscovery> peerDiscovery {};
79 StatusCallback statusChangedCallback {};
80 CertificateStoreQueryLegacy certificateStore {};
81 CertificateStoreQuery certificateStorePkId {};
82 IdentityAnnouncedCb identityAnnouncedCb {};
83 PublicAddressChangedCb publicAddressChangedCb {};
84 std::unique_ptr<std::mt19937_64> rng {};
85 Context() {}
86 };
87
88 DhtRunner();
89 virtual ~DhtRunner();
90
91 void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = {}, Where w = {}) {
92 get(id, bindGetCb(cb), donecb, f, w);
93 }
94
95 void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
96 get(id, bindGetCb(cb), donecb, f, w);
97 }
98
99 void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={}, Where w = {});
100
101 void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
102 get(id, cb, bindDoneCb(donecb), f, w);
103 }
104 void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = {}, Where w = {});
105
106 template <class T>
107 void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, DoneCallbackSimple dcb={})
108 {
109 get(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
110 return cb(unpackVector<T>(vals));
111 },
112 dcb,
113 getFilterSet<T>());
114 }
115 template <class T>
116 void get(InfoHash hash, std::function<bool(T&&)> cb, DoneCallbackSimple dcb={})
117 {
118 get(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
119 for (const auto& v : vals) {
120 try {
121 if (not cb(Value::unpack<T>(*v)))
122 return false;
123 } catch (const std::exception&) {
124 continue;
125 }
126 }
127 return true;
128 },
129 dcb,
130 getFilterSet<T>());
131 }
132
133 std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = {}, Where w = {}) {
134 auto p = std::make_shared<std::promise<std::vector<std::shared_ptr< dht::Value >>>>();
135 auto values = std::make_shared<std::vector<std::shared_ptr< dht::Value >>>();
136 get(key, [=](const std::vector<std::shared_ptr<dht::Value>>& vlist) {
137 values->insert(values->end(), vlist.begin(), vlist.end());
138 return true;
139 }, [=](bool) {
140 p->set_value(std::move(*values));
141 },
142 f, w);
143 return p->get_future();
144 }
145
146 template <class T>
147 std::future<std::vector<T>> get(InfoHash key) {
148 auto p = std::make_shared<std::promise<std::vector<T>>>();
149 auto values = std::make_shared<std::vector<T>>();
150 get<T>(key, [=](T&& v) {
151 values->emplace_back(std::move(v));
152 return true;
153 }, [=](bool) {
154 p->set_value(std::move(*values));
155 });
156 return p->get_future();
157 }
158
159 void query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb = {}, Query q = {});
160 void query(const InfoHash& hash, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query q = {}) {
161 query(hash, cb, bindDoneCb(done_cb), q);
162 }
163
164 std::future<size_t> listen(InfoHash key, ValueCallback vcb, Value::Filter f = {}, Where w = {});
165
166 std::future<size_t> listen(InfoHash key, GetCallback cb, Value::Filter f={}, Where w={}) {
167 return listen(key, [cb=std::move(cb)](const std::vector<Sp<Value>>& vals, bool expired){
168 if (not expired)
169 return cb(vals);
170 return true;
171 }, std::forward<Value::Filter>(f), std::forward<Where>(w));
172 }
173 std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = {}, Where w = {});
174 std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = {}, Where w = {}) {
175 return listen(key, bindGetCb(cb), f, w);
176 }
177
178 template <class T>
179 std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&)> cb)
180 {
181 return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
182 return cb(unpackVector<T>(vals));
183 },
184 getFilterSet<T>());
185 }
186 template <class T>
187 std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&, bool)> cb)
188 {
189 return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
190 return cb(unpackVector<T>(vals), expired);
191 },
192 getFilterSet<T>());
193 }
194
195 template <typename T>
196 std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = {}, Where w = {})
197 {
198 return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
199 for (const auto& v : vals) {
200 try {
201 if (not cb(Value::unpack<T>(*v)))
202 return false;
203 } catch (const std::exception&) {
204 continue;
205 }
206 }
207 return true;
208 },
209 getFilterSet<T>(f), w);
210 }
211 template <typename T>
212 std::future<size_t> listen(InfoHash hash, std::function<bool(T&&, bool)> cb, Value::Filter f = {}, Where w = {})
213 {
214 return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
215 for (const auto& v : vals) {
216 try {
217 if (not cb(Value::unpack<T>(*v), expired))
218 return false;
219 } catch (const std::exception&) {
220 continue;
221 }
222 }
223 return true;
224 },
225 getFilterSet<T>(f), w);
226 }
227
228 void cancelListen(InfoHash h, size_t token);
229 void cancelListen(InfoHash h, std::shared_future<size_t> token);
230
231 void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
232 void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
233 put(hash, value, bindDoneCb(cb), created, permanent);
234 }
235
236 void put(InfoHash hash, Value&& value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
237 void put(InfoHash hash, Value&& value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
238 put(hash, std::forward<Value>(value), bindDoneCb(cb), created, permanent);
239 }
240 void put(const std::string& key, Value&& value, DoneCallbackSimple cb={}, time_point created=time_point::max(), bool permanent = false);
241
242 void cancelPut(const InfoHash& h, Value::Id id);
243 void cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value);
244
245 void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
246 void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
247 putSigned(hash, value, bindDoneCb(cb), permanent);
248 }
249
250 void putSigned(InfoHash hash, Value&& value, DoneCallback cb={}, bool permanent = false);
251 void putSigned(InfoHash hash, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
252 putSigned(hash, std::forward<Value>(value), bindDoneCb(cb), permanent);
253 }
254 void putSigned(const std::string& key, Value&& value, DoneCallbackSimple cb={}, bool permanent = false);
255
256 void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
257 void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
258 putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
259 }
260
261 void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallback cb={}, bool permanent = false);
262 void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
263 putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb), permanent);
264 }
265 void putEncrypted(const std::string& key, InfoHash to, Value&& value, DoneCallback cb={}, bool permanent = false);
266
267 void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
268 void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
269 putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
270 }
271
272 [[deprecated("Use the shared_ptr version instead")]]
273 void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb={}, bool permanent = false);
274 [[deprecated("Use the shared_ptr version instead")]]
275 void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
276 putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb), permanent);
277 }
278
279 void putEncrypted(InfoHash hash, const PkId& to, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
280 void putEncrypted(InfoHash hash, const PkId& to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
281 putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
282 }
283
288 void bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb={});
289 void bootstrap(SockAddr addr, DoneCallbackSimple cb={});
290
295 void bootstrap(std::vector<NodeExport> nodes);
296
303 void bootstrap(const std::string& host, const std::string& service);
304 void bootstrap(const std::string& hostService);
305
310 void bootstrap(const InfoHash& id, const SockAddr& address);
311
316
323
324 void dumpTables() const;
325
329 [[deprecated("Use getPublicKey()->getLongId() instead")]] InfoHash getId() const;
330 std::shared_ptr<crypto::PublicKey> getPublicKey() const;
331
335 InfoHash getNodeId() const;
336
341 SockAddr getBound(sa_family_t f = AF_INET) const;
342
347 in_port_t getBoundPort(sa_family_t f = AF_INET) const;
348
349 std::pair<size_t, size_t> getStoreSize() const;
350
351 void getStorageLimit() const;
352 void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT);
353
354 std::vector<NodeExport> exportNodes() const;
355
356 std::vector<ValuesExport> exportValues() const;
357
358 void setLogger(const Sp<Logger>& logger = {});
359 void setLogger(const Logger& logger) {
360 setLogger(std::make_shared<Logger>(logger));
361 }
362
366 void setLogFilter(const InfoHash& f = {});
367
368 void registerType(const ValueType& type);
369
370 void importValues(const std::vector<ValuesExport>& values);
371
372 bool isRunning() const {
373 return running != State::Idle;
374 }
375
376 NodeStats getNodesStats(sa_family_t af) const;
377 unsigned getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
378 NodeInfo getNodeInfo() const;
379 void getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)>);
380
381 std::vector<unsigned> getNodeMessageStats(bool in = false) const;
382 std::string getStorageLog() const;
383 std::string getStorageLog(const InfoHash&) const;
384 std::string getRoutingTablesLog(sa_family_t af) const;
385 std::string getSearchesLog(sa_family_t af = AF_UNSPEC) const;
386 std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
387 std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC) const;
388 std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC) const;
389 void getPublicAddress(std::function<void(std::vector<SockAddr>&&)>, sa_family_t af = AF_UNSPEC);
390
391 // securedht methods
392
393 void findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
394 void findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
395
396 void registerCertificate(const std::shared_ptr<crypto::Certificate>& cert);
397 void setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method);
398 void setLocalCertificateStore(CertificateStoreQuery&& query_method);
399
406 void run(in_port_t port = dht::net::DHT_DEFAULT_PORT, const crypto::Identity& identity = {}, bool threaded = true, NetId network = 0) {
407 Config config;
408 config.dht_config.node_config.network = network;
409 config.dht_config.id = identity;
410 config.threaded = threaded;
411 run(port, config);
412 }
413 void run(in_port_t port, Config& config, Context&& context = {});
414
418 void run(const char* ip4, const char* ip6, const char* service, Config& config, Context&& context = {});
419
420 void run(const Config& config, Context&& context);
421
422 void setOnStatusChanged(StatusCallback&& cb) {
423 if (cb)
424 statusCbs.emplace_back(std::move(cb));
425 }
426
432 time_point loop() {
433 std::lock_guard<std::mutex> lck(dht_mtx);
434 return loop_();
435 }
436
440 void shutdown(ShutdownCallback cb = {}, bool stop = false);
441
449 void join();
450
451 std::shared_ptr<PeerDiscovery> getPeerDiscovery() const { return peerDiscovery_; };
452
453 void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
454
459 void enableProxy(bool proxify);
460
461 /* Push notification methods */
462
466 void setPushNotificationToken(const std::string& token);
467
471 void setPushNotificationTopic(const std::string& topic);
472
476 void setPushNotificationPlatform(const std::string& platform);
477
481 std::future<PushNotificationResult> pushNotificationReceived(const std::map<std::string, std::string>& data);
482
483 /* Proxy server mothods */
484 void forwardAllMessages(bool forward);
485
486private:
487 enum class State {
488 Idle,
489 Running,
490 Stopping
491 };
492
493 time_point loop_();
494
495 NodeStatus getStatus() const {
496 return std::max(status4, status6);
497 }
498
499 bool checkShutdown();
500 void opEnded();
501 DoneCallback bindOpDoneCallback(DoneCallback&& cb);
502 DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
503
505 std::unique_ptr<SecureDht> dht_;
506
508 std::atomic_bool use_proxy {false};
509
511 Config config_;
512 IdentityAnnouncedCb identityAnnouncedCb_;
513
517 void resetDht();
518
519 mutable std::mutex dht_mtx {};
520 std::thread dht_thread {};
521 std::condition_variable cv {};
522 std::mutex sock_mtx {};
523 net::PacketList rcv {};
524 decltype(rcv) rcv_free {};
525
526 std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
527 std::queue<std::function<void(SecureDht&)>> pending_ops {};
528 std::mutex storage_mtx {};
529
530 std::atomic<State> running {State::Idle};
531 std::atomic_size_t ongoing_ops {0};
532 std::vector<ShutdownCallback> shutdownCallbacks_;
533
534 NodeStatus status4 {NodeStatus::Disconnected},
535 status6 {NodeStatus::Disconnected};
536
537 std::vector<StatusCallback> statusCbs {};
538
540 std::shared_ptr<PeerDiscovery> peerDiscovery_;
541
546 std::shared_ptr<dht::Logger> logger_;
547};
548
549}
InfoHash getId() const
in_port_t getBoundPort(sa_family_t f=AF_INET) const
void clearBootstrap()
void shutdown(ShutdownCallback cb={}, bool stop=false)
void bootstrap(const std::string &host, const std::string &service)
void setPushNotificationToken(const std::string &token)
void connectivityChanged()
time_point loop()
Definition dhtrunner.h:432
InfoHash getNodeId() const
void run(in_port_t port=dht::net::DHT_DEFAULT_PORT, const crypto::Identity &identity={}, bool threaded=true, NetId network=0)
Definition dhtrunner.h:406
void bootstrap(std::vector< SockAddr > nodes, DoneCallbackSimple cb={})
void setLogFilter(const InfoHash &f={})
SockAddr getBound(sa_family_t f=AF_INET) const
void run(const char *ip4, const char *ip6, const char *service, Config &config, Context &&context={})
void enableProxy(bool proxify)
void setPushNotificationPlatform(const std::string &platform)
void bootstrap(const InfoHash &id, const SockAddr &address)
std::future< PushNotificationResult > pushNotificationReceived(const std::map< std::string, std::string > &data)
void setPushNotificationTopic(const std::string &topic)
void bootstrap(std::vector< NodeExport > nodes)
NodeStatus
Definition callbacks.h:42
NetId network
Definition callbacks.h:114