My Project 4.4.0
C++ Distributed Hash Table
Loading...
Searching...
No Matches
dhtrunner.h
1// Copyright (c) 2014-2026 Savoir-faire Linux Inc.
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "def.h"
6#include "infohash.h"
7#include "value.h"
8#include "callbacks.h"
9#include "sockaddr.h"
10#include "logger.h"
11#include "network_utils.h"
12#include "node_export.h"
13
14#include <thread>
15#include <mutex>
16#include <atomic>
17#include <condition_variable>
18#include <future>
19#include <exception>
20#include <queue>
21#include <chrono>
22
23namespace dht {
24
25struct Node;
26class SecureDht;
27class PeerDiscovery;
28struct SecureDhtConfig;
29
36class OPENDHT_PUBLIC DhtRunner
37{
38public:
39 using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
40
41 struct Config
42 {
43 SecureDhtConfig dht_config {};
44 bool threaded {true};
45 std::string proxy_server {};
46 std::string push_node_id {};
47 std::string push_token {};
48 std::string push_topic {};
49 std::string push_platform {};
50 std::string proxy_user_agent {};
51 bool peer_discovery {false};
52 bool peer_publish {false};
53 std::shared_ptr<dht::crypto::Certificate> server_ca;
54 dht::crypto::Identity client_identity;
55 SockAddr bind4 {}, bind6 {};
56 };
57
58 struct Context
59 {
60 std::shared_ptr<Logger> logger {};
61 std::unique_ptr<net::DatagramSocket> sock;
62 std::shared_ptr<PeerDiscovery> peerDiscovery {};
63 StatusCallback statusChangedCallback {};
64 CertificateStoreQuery certificateStore {};
65 IdentityAnnouncedCb identityAnnouncedCb {};
66 PublicAddressChangedCb publicAddressChangedCb {};
67 std::unique_ptr<std::mt19937_64> rng {};
68 Context() {}
69 };
70
71 DhtRunner();
72 virtual ~DhtRunner();
73
74 void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb = {}, Value::Filter f = {}, Where w = {})
75 {
76 get(id, bindGetCb(cb), donecb, f, w);
77 }
78
79 void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb = {}, Value::Filter f = {}, Where w = {})
80 {
81 get(id, bindGetCb(cb), donecb, f, w);
82 }
83
84 void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f = {}, Where w = {});
85
86 void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb = {}, Value::Filter f = {}, Where w = {})
87 {
88 get(id, cb, bindDoneCb(donecb), f, w);
89 }
90 void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb = {}, Value::Filter f = {}, Where w = {});
91
92 template<class T>
93 void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, DoneCallbackSimple dcb = {})
94 {
95 get(
96 hash,
97 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) { return cb(unpackVector<T>(vals)); },
98 dcb,
99 getFilterSet<T>());
100 }
101 template<class T>
102 void get(InfoHash hash, std::function<bool(T&&)> cb, DoneCallbackSimple dcb = {})
103 {
104 get(
105 hash,
106 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
107 for (const auto& v : vals) {
108 try {
109 if (not cb(Value::unpack<T>(*v)))
110 return false;
111 } catch (const std::exception&) {
112 continue;
113 }
114 }
115 return true;
116 },
117 dcb,
118 getFilterSet<T>());
119 }
120
121 std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = {}, Where w = {})
122 {
123 auto p = std::make_shared<std::promise<std::vector<std::shared_ptr<dht::Value>>>>();
124 auto values = std::make_shared<std::vector<std::shared_ptr<dht::Value>>>();
125 get(
126 key,
127 [=](const std::vector<std::shared_ptr<dht::Value>>& vlist) {
128 values->insert(values->end(), vlist.begin(), vlist.end());
129 return true;
130 },
131 [=](bool) { p->set_value(std::move(*values)); },
132 f,
133 w);
134 return p->get_future();
135 }
136
137 template<class T>
138 std::future<std::vector<T>> get(InfoHash key)
139 {
140 auto p = std::make_shared<std::promise<std::vector<T>>>();
141 auto values = std::make_shared<std::vector<T>>();
142 get<T>(
143 key,
144 [=](T&& v) {
145 values->emplace_back(std::move(v));
146 return true;
147 },
148 [=](bool) { p->set_value(std::move(*values)); });
149 return p->get_future();
150 }
151
152 void query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb = {}, Query q = {});
153 void query(const InfoHash& hash, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query q = {})
154 {
155 query(hash, cb, bindDoneCb(done_cb), q);
156 }
157
158 std::future<size_t> listen(InfoHash key, ValueCallback vcb, Value::Filter f = {}, Where w = {});
159
160 std::future<size_t> listen(InfoHash key, GetCallback cb, Value::Filter f = {}, Where w = {})
161 {
162 return listen(
163 key,
164 [cb = std::move(cb)](const std::vector<Sp<Value>>& vals, bool expired) {
165 if (not expired)
166 return cb(vals);
167 return true;
168 },
169 std::forward<Value::Filter>(f),
170 std::forward<Where>(w));
171 }
172 std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = {}, Where w = {});
173 std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = {}, Where w = {})
174 {
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(
182 hash,
183 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) { return cb(unpackVector<T>(vals)); },
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(
190 hash,
191 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
192 return cb(unpackVector<T>(vals), expired);
193 },
194 getFilterSet<T>());
195 }
196
197 template<typename T>
198 std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = {}, Where w = {})
199 {
200 return listen(
201 hash,
202 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
203 for (const auto& v : vals) {
204 try {
205 if (not cb(Value::unpack<T>(*v)))
206 return false;
207 } catch (const std::exception&) {
208 continue;
209 }
210 }
211 return true;
212 },
213 getFilterSet<T>(f),
214 w);
215 }
216 template<typename T>
217 std::future<size_t> listen(InfoHash hash, std::function<bool(T&&, bool)> cb, Value::Filter f = {}, Where w = {})
218 {
219 return listen(
220 hash,
221 [cb = std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
222 for (const auto& v : vals) {
223 try {
224 if (not cb(Value::unpack<T>(*v), expired))
225 return false;
226 } catch (const std::exception&) {
227 continue;
228 }
229 }
230 return true;
231 },
232 getFilterSet<T>(f),
233 w);
234 }
235
236 void cancelListen(InfoHash h, size_t token);
237 void cancelListen(InfoHash h, std::shared_future<size_t> token);
238
239 void put(InfoHash hash,
240 std::shared_ptr<Value> value,
241 DoneCallback cb = {},
242 time_point created = time_point::max(),
243 bool permanent = false);
244 void put(InfoHash hash,
245 std::shared_ptr<Value> value,
246 DoneCallbackSimple cb,
247 time_point created = time_point::max(),
248 bool permanent = false)
249 {
250 put(hash, value, bindDoneCb(cb), created, permanent);
251 }
252
253 void put(InfoHash hash,
254 Value&& value,
255 DoneCallback cb = {},
256 time_point created = time_point::max(),
257 bool permanent = false);
258 void put(InfoHash hash,
259 Value&& value,
260 DoneCallbackSimple cb,
261 time_point created = time_point::max(),
262 bool permanent = false)
263 {
264 put(hash, std::forward<Value>(value), bindDoneCb(cb), created, permanent);
265 }
266 void put(const std::string& key,
267 Value&& value,
268 DoneCallbackSimple cb = {},
269 time_point created = time_point::max(),
270 bool permanent = false);
271
272 void cancelPut(const InfoHash& h, Value::Id id);
273 void cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value);
274
275 void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb = {}, bool permanent = false);
276 void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false)
277 {
278 putSigned(hash, value, bindDoneCb(cb), permanent);
279 }
280
281 void putSigned(InfoHash hash, Value&& value, DoneCallback cb = {}, bool permanent = false);
282 void putSigned(InfoHash hash, Value&& value, DoneCallbackSimple cb, bool permanent = false)
283 {
284 putSigned(hash, std::forward<Value>(value), bindDoneCb(cb), permanent);
285 }
286 void putSigned(const std::string& key, Value&& value, DoneCallbackSimple cb = {}, bool permanent = false);
287
288 void putEncrypted(InfoHash hash,
289 const std::shared_ptr<crypto::PublicKey>& to,
290 std::shared_ptr<Value> value,
291 DoneCallback cb = {},
292 bool permanent = false);
293 void putEncrypted(InfoHash hash,
294 const std::shared_ptr<crypto::PublicKey>& to,
295 std::shared_ptr<Value> value,
296 DoneCallbackSimple cb,
297 bool permanent = false)
298 {
299 putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
300 }
301
302 void putEncrypted(InfoHash hash,
303 const std::shared_ptr<crypto::PublicKey>& to,
304 Value&& value,
305 DoneCallback cb = {},
306 bool permanent = false);
307 void putEncrypted(InfoHash hash,
308 const std::shared_ptr<crypto::PublicKey>& to,
309 Value&& value,
310 DoneCallbackSimple cb,
311 bool permanent = false)
312 {
313 putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb), permanent);
314 }
315
316 void putEncrypted(
317 InfoHash hash, const PkId& to, std::shared_ptr<Value> value, DoneCallback cb = {}, bool permanent = false);
318 void putEncrypted(
319 InfoHash hash, const PkId& to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false)
320 {
321 putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
322 }
323 void putEncrypted(InfoHash hash, const PkId& to, Value&& value, DoneCallback cb = {}, bool permanent = false)
324 {
325 putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), std::move(cb), permanent);
326 }
327 void putEncrypted(InfoHash hash, const PkId& to, Value&& value, DoneCallbackSimple cb, bool permanent = false)
328 {
329 putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), bindDoneCb(cb), permanent);
330 }
331
336 void bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb = {});
337 void bootstrap(SockAddr addr, DoneCallbackSimple cb = {});
338
343 void bootstrap(std::vector<NodeExport> nodes);
344
351 void bootstrap(const std::string& host, const std::string& service);
352 void bootstrap(const std::string& hostService);
353
358 void bootstrap(const InfoHash& id, const SockAddr& address);
359
364
371
372 void dumpTables() const;
373
377 [[deprecated("Use getPublicKey()->getLongId() instead")]] InfoHash getId() const;
378 std::shared_ptr<crypto::PublicKey> getPublicKey() const;
379
383 InfoHash getNodeId() const;
384
389 SockAddr getBound(sa_family_t f = AF_INET) const;
390
395 in_port_t getBoundPort(sa_family_t f = AF_INET) const;
396
397 std::pair<size_t, size_t> getStoreSize() const;
398
399 size_t getStorageLimit() const;
400 void setStorageLimit(size_t limit = 0);
401
402 size_t getLocalStorageLimit() const;
403 void setLocalStorageLimit(size_t limit = 0);
404
405 std::vector<NodeExport> exportNodes() const;
406
407 std::vector<ValuesExport> exportValues() const;
408
409 void setLogger(const Sp<Logger>& logger = {});
410
414 void setLogFilter(const InfoHash& f = {});
415
416 void registerType(const ValueType& type);
417 void registerInsecureType(const ValueType& type);
418
419 void importValues(const std::vector<ValuesExport>& values);
420
421 bool isRunning() const { return running != State::Idle; }
422
423 NodeStats getNodesStats(sa_family_t af) const;
424 unsigned getNodesStats(sa_family_t af,
425 unsigned* good_return,
426 unsigned* dubious_return,
427 unsigned* cached_return,
428 unsigned* incoming_return) const;
429 NodeInfo getNodeInfo() const;
430 void getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)>);
431
432 std::vector<unsigned> getNodeMessageStats(bool in = false) const;
433 std::string getStorageLog() const;
434 std::string getStorageLog(const InfoHash&) const;
435 std::string getRoutingTablesLog(sa_family_t af) const;
436 std::string getSearchesLog(sa_family_t af = AF_UNSPEC) const;
437 std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
438 std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC) const;
439 std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC) const;
440 void getPublicAddress(std::function<void(std::vector<SockAddr>&&)>, sa_family_t af = AF_UNSPEC);
441
442 // securedht methods
443 void findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
444
445 void registerCertificate(const std::shared_ptr<crypto::Certificate>& cert);
446 void setLocalCertificateStore(CertificateStoreQuery&& query_method);
447
454 void run(in_port_t port = dht::net::DHT_DEFAULT_PORT,
455 const crypto::Identity& identity = {},
456 bool threaded = true,
457 NetId network = 0)
458 {
459 Config config;
460 config.dht_config.node_config.network = network;
461 config.dht_config.id = identity;
462 config.threaded = threaded;
463 run(port, config);
464 }
465 void run(in_port_t port, Config& config, Context&& context = {});
466
471 void run(const char* ip4, const char* ip6, const char* service, Config& config, Context&& context = {});
472
473 void run(const Config& config, Context&& context);
474
475 void setOnStatusChanged(StatusCallback&& cb)
476 {
477 if (cb)
478 statusCbs.emplace_back(std::move(cb));
479 }
480
486 time_point loop()
487 {
488 std::lock_guard lck(dht_mtx);
489 return loop_();
490 }
491
495 void shutdown(ShutdownCallback cb = {}, bool stop = false);
496
504 void join();
505
506 std::shared_ptr<PeerDiscovery> getPeerDiscovery() const { return peerDiscovery_; };
507
508 void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
509
514 void enableProxy(bool proxify);
515
516 /* Push notification methods */
517
521 void setPushNotificationToken(const std::string& token);
522
526 void setPushNotificationTopic(const std::string& topic);
527
531 void setPushNotificationPlatform(const std::string& platform);
532
536 std::future<PushNotificationResult> pushNotificationReceived(const std::map<std::string, std::string>& data);
537
538 /* Proxy server mothods */
539 void forwardAllMessages(bool forward);
540
541private:
542 enum class State { Idle, Running, Stopping };
543
544 time_point loop_();
545
546 NodeStatus getStatus() const { return std::max(status4, status6); }
547
548 bool checkShutdown();
549 void opEnded();
550 DoneCallback bindOpDoneCallback(DoneCallback&& cb);
551 DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
552
554 std::unique_ptr<SecureDht> dht_;
555
557 std::atomic_bool use_proxy {false};
558
560 Config config_;
561 IdentityAnnouncedCb identityAnnouncedCb_;
562
566 void resetDht();
567
568 mutable std::mutex dht_mtx;
569 std::thread dht_thread {};
570 std::condition_variable cv {};
571 std::mutex sock_mtx;
572 net::PacketList rcv {};
573 decltype(rcv) rcv_free {};
574
575 std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
576 std::queue<std::function<void(SecureDht&)>> pending_ops {};
577 std::mutex storage_mtx;
578
579 std::atomic<State> running {State::Idle};
580 std::atomic_size_t ongoing_ops {0};
581 std::vector<ShutdownCallback> shutdownCallbacks_;
582
583 NodeStatus status4 {NodeStatus::Disconnected}, status6 {NodeStatus::Disconnected};
584
585 std::vector<StatusCallback> statusCbs {};
586
588 std::shared_ptr<PeerDiscovery> peerDiscovery_;
589
594 std::shared_ptr<dht::Logger> logger_;
595};
596
597} // namespace dht
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:486
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:454
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:24
NetId network
Definition callbacks.h:108