Back to Site
Loading...
Searching...
No Matches
dht_discovery.h
Go to the documentation of this file.
1#pragma once
2
57
58#include "librats/util/rats_export.h"
61#include "librats/dht/dht.h"
62
63#include <atomic>
64#include <chrono>
65#include <condition_variable>
66#include <cstdint>
67#include <memory>
68#include <mutex>
69#include <optional>
70#include <string>
71#include <thread>
72#include <unordered_map>
73#include <vector>
74
75namespace librats {
76
77class DialService;
78class ReflexiveAddressService;
79
80class RATS_API DhtDiscovery final : public Subsystem, public DhtService {
81public:
82 struct Config {
83 uint16_t dht_port = 0;
84 std::string bind_address = "";
85 std::string data_dir = "";
86 bool enable_ipv4 = true;
87 bool enable_ipv6 = true;
88 std::string discovery_key = "";
91 std::vector<std::string> keys;
92 std::vector<HostEndpoint> bootstrap_nodes;
93 std::chrono::milliseconds search_interval{30000};
98 std::chrono::milliseconds announce_interval{600000}; // 10 min
99
105 std::vector<HostEndpoint> stun_servers;
106 std::chrono::milliseconds stun_timeout{3000};
110 std::chrono::milliseconds stun_every{20000};
111
118 std::chrono::milliseconds redial_every{5000};
119 std::chrono::milliseconds redial_for{120000};
126 std::chrono::milliseconds redial_slow{25000};
127 };
128
130 class RATS_API Redials {
131 public:
132 enum class Dial : uint8_t {
133 None,
134 First,
135 Again,
136 };
137 using Clock = std::chrono::steady_clock;
138
141 Redials(std::chrono::milliseconds every, std::chrono::milliseconds dial_for,
142 std::chrono::milliseconds slow)
143 : every_(every), for_(dial_for), slow_(slow) {}
144
146 Dial found(const Address& address, Clock::time_point now);
150 std::vector<Address> due(Clock::time_point now);
152 std::optional<Clock::time_point> next_due() const;
155 void connected(const Address& address) { found_.erase(address); }
158 void forget_older_than(Clock::time_point before);
159 size_t size() const { return found_.size(); }
160
161 private:
162 struct Dialed {
163 Clock::time_point first;
164 Clock::time_point last;
165 Clock::time_point seen;
166 };
167 std::chrono::milliseconds every_;
168 std::chrono::milliseconds for_;
169 std::chrono::milliseconds slow_;
170 std::unordered_map<Address, Dialed> found_;
171 };
172
173 explicit DhtDiscovery(Config config);
174 ~DhtDiscovery() override;
175
176 void attach(NodeContext& ctx) override;
177 void start() override;
178 void stop() override;
179
180 bool is_running() const;
181
185 DhtClient* dht_client() override { return dht_ ? dht_.get() : dht6_.get(); }
186
187 uint16_t dht_port() const;
188 uint16_t dht_port_v6() const;
190 InfoHash discovery_hash() const;
191 std::vector<InfoHash> discovery_hashes() const;
192
199 enum class Use : uint8_t { Announce = 1, Search = 2, Both = 3 };
200 struct Key {
201 std::string key;
203 };
204
209 void set_keys(const std::vector<Key>& keys);
210 void set_keys(const std::vector<std::string>& keys);
211
214 std::string external_address() const;
215
224 bool is_own(const Address& address) const;
225
227 static InfoHash hash_for_key(const std::string& key);
228
229private:
230 void loop();
233 bool probe_external_ip();
234 void on_peers(const std::vector<Address>& peers, const InfoHash& info_hash);
237 bool searches(const InfoHash& hash) const;
241 std::vector<Address> connected_addresses() const;
243 void punch_due(std::chrono::steady_clock::time_point now);
245 uint16_t announced_port(bool v6 = false) const;
246
249 std::unique_ptr<DhtClient> make_client(AddressFamily family);
250
253 void wake();
254
256 void refresh_own_ips();
257
259 template <typename Fn>
260 void for_each_client(Fn fn) {
261 if (dht_) fn(*dht_);
262 if (dht6_) fn(*dht6_);
263 }
264
267 struct Announced {
268 InfoHash hash{};
269 Use use = Use::Both;
270 std::optional<std::chrono::steady_clock::time_point> announced[2];
271 bool searched = false;
272 };
273
274 Config config_;
275 mutable std::mutex keys_mutex_;
276 std::vector<Announced> hashes_;
277 bool keys_set_ = false;
278 PeerNetwork* network_ = nullptr;
279 DialService* dial_ = nullptr;
280 ReflexiveAddressService* reflexive_ = nullptr;
281 std::unique_ptr<DhtClient> dht_;
282 std::unique_ptr<DhtClient> dht6_;
283
284 std::thread thread_;
285 std::atomic<bool> running_{false};
286 std::atomic<bool> recover_pending_{false};
287 std::atomic<bool> announce_pending_{false};
288 std::atomic<bool> keys_pending_{false};
289 std::atomic<bool> punch_pending_{false};
290 std::atomic<uint16_t> mapped_port_{0};
291 std::atomic<uint16_t> reflexive_port_{0};
292 bool mapping_checked_ = false;
293 std::optional<Address> last_seen_;
294
295 mutable std::mutex own_mutex_;
296 std::vector<IpAddress> own_local_;
297 std::optional<IpAddress> own_seen_;
298 std::optional<IpAddress> own_mapped_;
299 std::mutex wait_mutex_;
300 std::condition_variable wake_;
301
302 std::mutex dialed_mutex_;
303 Redials dialed_;
304};
305
306} // namespace librats
Redials(std::chrono::milliseconds every, std::chrono::milliseconds dial_for, std::chrono::milliseconds slow)
Punched every every for dial_for after first found (due()); then, as found again, every slow at most ...
void connected(const Address &address)
address found connected: once its link is gone, it is dialed as though found for the first time.
std::optional< Clock::time_point > next_due() const
When the next of those is due; none: none is in its first stretch.
void forget_older_than(Clock::time_point before)
Forget what has not been found for a while: the DHT stops handing out an address half an hour after i...
std::chrono::steady_clock Clock
std::vector< Address > due(Clock::time_point now)
The addresses whose punch is due at now in their first stretch (dial_for after first found),...
Dial found(const Address &address, Clock::time_point now)
address found at now, not connected.
~DhtDiscovery() override
bool is_own(const Address &address) const
Whether address is this node's own, as a key it announces and searches hands it back: an IP of this h...
void set_keys(const std::vector< std::string > &keys)
std::vector< InfoHash > discovery_hashes() const
InfoHash discovery_hash() const
The first key's hash (the only one, unless keys were given).
uint16_t dht_port_v6() const
IPv6 DHT UDP port (0 if not running).
std::string external_address() const
Our external (public) IP currently used to derive the DHT node id, learned via STUN at startup or in-...
void set_keys(const std::vector< Key > &keys)
The keys announced and searched from now on, in place of those before (a key twice: what both say): o...
void stop() override
DhtDiscovery(Config config)
static InfoHash hash_for_key(const std::string &key)
Map an application key to a stable 20-byte discovery hash (SHA-1).
uint16_t dht_port() const
IPv4 DHT UDP port (0 if not running).
DhtClient * dht_client() override
DhtService: hand out the live Kademlia node so siblings (e.g.
Use
What is done with a key: announced, so that the node is found under it; searched, so that it finds wh...
void start() override
void attach(NodeContext &ctx) override
bool is_running() const
A pluggable network subsystem.
Capability that hands out the node-owned DhtClient to sibling modules.
Definition node.h:75
The narrow contract a subsystem needs from the node — and nothing more.
std::chrono::milliseconds stun_every
STUN is asked again this often, on the search round it falls in — after a round with no answer,...
std::vector< std::string > keys
The keys to announce and search, in place of discovery_key when not empty (set_keys() changes them as...
std::string data_dir
routing-table persistence dir (empty = cwd). Set to the node's data_dir to co-locate state.
bool enable_ipv4
run the IPv4 Kademlia network
std::vector< HostEndpoint > bootstrap_nodes
empty → default internet nodes
uint16_t dht_port
0 = ephemeral
std::chrono::milliseconds announce_interval
every 30 seconds
std::chrono::milliseconds search_interval
std::vector< HostEndpoint > stun_servers
empty → built-in public defaults
std::chrono::milliseconds redial_for
bool enable_ipv6
run the IPv6 Kademlia network (BEP 32)
std::chrono::milliseconds redial_slow
After redial_for: punched again each time the DHT hands it out again, at most this often,...
bool discover_external_ip
Ask STUN where we are: the public IP seeds the DHT node id per BEP 42 (without it we still converge v...
std::chrono::milliseconds redial_every
A peer found and not connected is punched this often, found again or not, for this long after it was ...
std::string discovery_key
DHT namespace.
std::chrono::milliseconds stun_timeout