169 DhtClient(
int port =
DHT_PORT,
const std::string& bind_address =
"",
const std::string& data_directory =
"");
197 bool bootstrap(
const std::vector<Peer>& bootstrap_nodes);
273#ifdef RATS_SEARCH_FEATURES
365 std::string bind_address_;
366 std::string data_directory_;
369 std::atomic<bool> running_;
385 std::vector<std::vector<DhtNode>> routing_table_;
386 mutable std::mutex routing_table_mutex_;
391 std::chrono::steady_clock::time_point created_at;
393 PeerToken() : created_at(
std::chrono::steady_clock::now()) {}
394 PeerToken(
const std::string& t)
395 : token(t), created_at(
std::chrono::steady_clock::now()) {}
397 std::unordered_map<Peer, PeerToken> peer_tokens_;
398 std::mutex peer_tokens_mutex_;
403 struct PendingSearch {
405 std::chrono::steady_clock::time_point created_at;
408 std::vector<DhtNode> search_nodes;
409 std::vector<Peer> found_peers;
412 std::unordered_map<NodeId, uint8_t> node_states;
419 std::vector<PeerDiscoveryCallback> callbacks;
423 std::unordered_map<NodeId, std::string> write_tokens;
425 uint16_t announce_port;
428 : info_hash(hash), created_at(
std::chrono::steady_clock::now()),
429 invoke_count(0), branch_factor(
ALPHA), is_finished(false),
430 is_announce(false), announce_port(0) {}
432 std::unordered_map<std::string, PendingSearch> pending_searches_;
433 mutable std::mutex pending_searches_mutex_;
436 struct SearchTransaction {
437 std::string info_hash_hex;
439 std::chrono::steady_clock::time_point sent_at;
441 SearchTransaction() =
default;
442 SearchTransaction(
const std::string& hash,
const NodeId&
id)
443 : info_hash_hex(hash), queried_node_id(id),
444 sent_at(
std::chrono::steady_clock::now()) {}
446 std::unordered_map<std::string, SearchTransaction> transaction_to_search_;
449 struct AnnouncedPeer {
451 std::chrono::steady_clock::time_point announced_at;
453 AnnouncedPeer(
const Peer& p)
454 : peer(p), announced_at(
std::chrono::steady_clock::now()) {}
457 std::unordered_map<std::string, std::vector<AnnouncedPeer>> announced_peers_;
458 std::mutex announced_peers_mutex_;
465 struct PingVerification {
466 DhtNode candidate_node;
469 std::chrono::steady_clock::time_point ping_sent_at;
471 PingVerification(
const DhtNode& candidate,
const DhtNode& old,
int bucket_idx)
472 : candidate_node(candidate), old_node(old), bucket_index(bucket_idx),
473 ping_sent_at(
std::chrono::steady_clock::now()) {}
475 std::unordered_map<std::string, PingVerification> pending_pings_;
476 std::unordered_set<NodeId> nodes_being_replaced_;
477 mutable std::mutex pending_pings_mutex_;
480 std::thread network_thread_;
481 std::thread maintenance_thread_;
484 std::condition_variable shutdown_cv_;
485 std::mutex shutdown_mutex_;
487#ifdef RATS_SEARCH_FEATURES
489 std::atomic<bool> spider_mode_{
false};
490 std::atomic<bool> spider_ignore_{
false};
492 std::mutex spider_callbacks_mutex_;
495 std::vector<DhtNode> spider_nodes_;
496 std::unordered_set<NodeId> spider_visited_;
497 mutable std::mutex spider_nodes_mutex_;
498 static constexpr size_t MAX_SPIDER_NODES = 2000;
499 static constexpr size_t MAX_SPIDER_VISITED = 10000;
500 static constexpr size_t MAX_SPIDER_CONTACTED_IPS = 10000;
504 std::unordered_map<std::string, std::chrono::steady_clock::time_point> spider_transactions_;
508 std::unordered_set<std::string> spider_contacted_ips_;
511 void add_spider_node(
const DhtNode& node);
512 void add_spider_nodes(
const std::vector<DhtNode>& nodes);
513 bool is_spider_node_visited(
const NodeId&
id)
const;
514 void mark_spider_node_visited(
const NodeId&
id);
515 void cleanup_spider_state();
516 void cleanup_stale_spider_transactions();
517 void mark_spider_transaction(
const std::string& transaction_id);
518 bool is_spider_transaction(
const std::string& transaction_id);
519 void mark_spider_contacted_ip(
const std::string& ip);
520 bool is_spider_contacted_ip(
const std::string& ip)
const;
525 void maintenance_loop();
526 void handle_message(
const std::vector<uint8_t>& data,
const Peer& sender);
531 void handle_krpc_message(
const KrpcMessage& message,
const Peer& sender);
532 void handle_krpc_ping(
const KrpcMessage& message,
const Peer& sender);
533 void handle_krpc_find_node(
const KrpcMessage& message,
const Peer& sender);
534 void handle_krpc_get_peers(
const KrpcMessage& message,
const Peer& sender);
535 void handle_krpc_announce_peer(
const KrpcMessage& message,
const Peer& sender);
536 void handle_krpc_response(
const KrpcMessage& message,
const Peer& sender);
537 void handle_krpc_error(
const KrpcMessage& message,
const Peer& sender);
540 bool send_krpc_message(
const KrpcMessage& message,
const Peer& peer);
541 void send_krpc_ping(
const Peer& peer);
542 void send_krpc_find_node(
const Peer& peer,
const NodeId& target);
543 void send_krpc_get_peers(
const Peer& peer,
const InfoHash& info_hash);
544 void send_krpc_announce_peer(
const Peer& peer,
const InfoHash& info_hash, uint16_t port,
const std::string& token);
546 void add_node(
const DhtNode& node,
bool confirmed =
true,
bool no_verify =
false);
547 std::vector<DhtNode> find_closest_nodes(
const NodeId& target,
size_t count =
K_BUCKET_SIZE);
548 std::vector<DhtNode> find_closest_nodes_unlocked(
const NodeId& target,
size_t count =
K_BUCKET_SIZE);
549 int get_bucket_index(
const NodeId&
id);
551 NodeId generate_node_id();
565 std::string generate_token(
const Peer& peer);
566 bool verify_token(
const Peer& peer,
const std::string& token);
570 void cleanup_stale_nodes();
571 void cleanup_stale_peer_tokens();
572 void refresh_buckets();
573 void print_statistics();
576 void cleanup_stale_searches();
577 void cleanup_timed_out_search_requests();
578 void cleanup_search_node_states();
579 void handle_get_peers_response_for_search(
const std::string& transaction_id,
const Peer& responder,
const std::vector<Peer>& peers);
580 void handle_get_peers_response_with_nodes(
const std::string& transaction_id,
const Peer& responder,
const std::vector<KrpcNode>& nodes);
581 void handle_get_peers_empty_response(
const std::string& transaction_id,
const Peer& responder);
582 void save_write_token(PendingSearch& search,
const NodeId& node_id,
const std::string& token);
583 bool add_search_requests(PendingSearch& search, DeferredCallbacks& deferred);
584 void add_node_to_search(PendingSearch& search,
const DhtNode& node);
585 void send_announce_to_closest_nodes(PendingSearch& search);
588 void store_announced_peer(
const InfoHash& info_hash,
const Peer& peer);
589 std::vector<Peer> get_announced_peers(
const InfoHash& info_hash);
590 void cleanup_stale_announced_peers();
593 void initiate_ping_verification(
const DhtNode& candidate_node,
const DhtNode& old_node,
int bucket_index);
594 void handle_ping_verification_response(
const std::string& transaction_id,
const NodeId& responder_id,
const Peer& responder);
595 void cleanup_stale_ping_verifications();
596 bool perform_replacement(
const DhtNode& candidate_node,
const DhtNode& node_to_replace,
int bucket_index);
599 static KrpcNode dht_node_to_krpc_node(
const DhtNode& node);
600 static DhtNode krpc_node_to_dht_node(
const KrpcNode& node);
601 static std::vector<KrpcNode> dht_nodes_to_krpc_nodes(
const std::vector<DhtNode>& nodes);
602 static std::vector<DhtNode> krpc_nodes_to_dht_nodes(
const std::vector<KrpcNode>& nodes);