librats 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +9 -4
  2. package/lib/index.d.ts +9 -22
  3. package/native-src/CMakeLists.txt +132 -50
  4. package/native-src/cmake/ratsConfig.cmake.in +8 -0
  5. package/native-src/src/bt_network.cpp +296 -183
  6. package/native-src/src/bt_network.h +25 -5
  7. package/native-src/src/crypto/sha256.c +1 -1
  8. package/native-src/src/dht.cpp +297 -47
  9. package/native-src/src/dht.h +70 -6
  10. package/native-src/src/file_transfer.cpp +1185 -1578
  11. package/native-src/src/file_transfer.h +240 -521
  12. package/native-src/src/io_poller.cpp +917 -0
  13. package/native-src/src/io_poller.h +138 -0
  14. package/native-src/src/krpc.cpp +161 -96
  15. package/native-src/src/krpc.h +18 -4
  16. package/native-src/src/librats.cpp +812 -1236
  17. package/native-src/src/librats.h +207 -208
  18. package/native-src/src/librats_bittorrent.cpp +1 -5
  19. package/native-src/src/librats_c.cpp +22 -39
  20. package/native-src/src/librats_discovery.cpp +377 -0
  21. package/native-src/src/librats_encryption.cpp +130 -283
  22. package/native-src/src/librats_file_transfer.cpp +27 -109
  23. package/native-src/src/librats_gossipsub.cpp +1 -5
  24. package/native-src/src/librats_ice.cpp +5 -1
  25. package/native-src/src/librats_log_macros.h +36 -0
  26. package/native-src/src/librats_logging.cpp +1 -7
  27. package/native-src/src/librats_mdns.cpp +1 -11
  28. package/native-src/src/librats_persistence.cpp +1 -11
  29. package/native-src/src/librats_reconnection.cpp +2 -13
  30. package/native-src/src/librats_statistic.cpp +105 -0
  31. package/native-src/src/socket.cpp +15 -3
  32. package/package.json +1 -1
  33. package/scripts/build-librats.js +3 -0
  34. package/scripts/prepare-package.js +10 -0
  35. package/src/librats_node.cpp +53 -64
@@ -13,6 +13,7 @@
13
13
  #include <atomic>
14
14
  #include <chrono>
15
15
  #include <memory>
16
+ #include <random>
16
17
  #include <condition_variable>
17
18
 
18
19
  // Hash specialization for Peer and NodeId (must be defined before use in unordered_map/set)
@@ -165,8 +166,12 @@ public:
165
166
  * Constructor
166
167
  * @param port The UDP port to bind to (default: 6881)
167
168
  * @param bind_address The interface IP address to bind to (empty for all interfaces)
169
+ * @param data_directory Directory for routing-table persistence
170
+ * @param address_family Address family this node operates on. IPv4 and IPv6 form
171
+ * separate Kademlia networks (BEP 32), so each family needs its own DhtClient.
168
172
  */
169
- DhtClient(int port = DHT_PORT, const std::string& bind_address = "", const std::string& data_directory = "");
173
+ DhtClient(int port = DHT_PORT, const std::string& bind_address = "", const std::string& data_directory = "",
174
+ AddressFamily address_family = AddressFamily::IPv4);
170
175
 
171
176
  /**
172
177
  * Destructor
@@ -222,9 +227,33 @@ public:
222
227
 
223
228
  /**
224
229
  * Get our node ID
225
- * @return The node ID
230
+ * @return The node ID (returned by value; the ID may be regenerated at runtime
231
+ * when our external IP is learned, see set_external_ip)
226
232
  */
227
- const NodeId& get_node_id() const { return node_id_; }
233
+ NodeId get_node_id() const { return node_id_; }
234
+
235
+ /**
236
+ * Set our external (public) IP address and regenerate the node ID from it per BEP 42.
237
+ * IPv4/IPv6 node IDs must be derived from the matching-family external address, so this
238
+ * is a no-op if the address family does not match this instance. Safe to call at runtime;
239
+ * the routing table is re-bucketed against the new ID. Non-public/invalid addresses are ignored.
240
+ * @param ip External IP address as seen by the network (e.g. from STUN or DHT "ip" voting)
241
+ */
242
+ void set_external_ip(const std::string& ip);
243
+
244
+ /**
245
+ * Get the external address currently used to derive our node ID ("" if none/random).
246
+ */
247
+ std::string get_external_address() const;
248
+
249
+ /**
250
+ * BEP 42 helpers (public for testing / explicit use).
251
+ * generate_node_id_from_ip: build an IP-derived node ID (random where BEP 42 allows).
252
+ * verify_node_id_for_ip: check whether a node ID is valid for the given source IP
253
+ * (always true for non-public IPs, which cannot be verified).
254
+ */
255
+ static bool generate_node_id_from_ip(const std::string& ip, NodeId& out, std::mt19937& gen);
256
+ static bool verify_node_id_for_ip(const NodeId& id, const std::string& ip);
228
257
 
229
258
  /**
230
259
  * Get number of nodes in routing table
@@ -337,11 +366,26 @@ public:
337
366
  #endif // RATS_SEARCH_FEATURES
338
367
 
339
368
  /**
340
- * Get default BitTorrent DHT bootstrap nodes
369
+ * Get default BitTorrent DHT bootstrap nodes.
370
+ * Hostnames are resolved per-family at send time, so the same list serves both
371
+ * IPv4 and IPv6 (nodes without an AAAA record are simply skipped on IPv6).
341
372
  * @return Vector of bootstrap nodes
342
373
  */
343
374
  static std::vector<Peer> get_default_bootstrap_nodes();
375
+
376
+ /**
377
+ * Address family this DHT node operates on.
378
+ */
379
+ AddressFamily address_family() const { return address_family_; }
380
+ bool is_ipv6() const { return address_family_ == AddressFamily::IPv6; }
344
381
 
382
+ /**
383
+ * Compute the routing-table persistence file path for this instance.
384
+ * Includes the address family so the IPv4 and IPv6 nodes (which may share a port)
385
+ * never overwrite each other's saved state.
386
+ */
387
+ std::string routing_table_file_path() const;
388
+
345
389
  /**
346
390
  * Save routing table to disk
347
391
  * @return true if successful, false otherwise
@@ -364,10 +408,23 @@ private:
364
408
  int port_;
365
409
  std::string bind_address_;
366
410
  std::string data_directory_;
367
- NodeId node_id_;
411
+ AddressFamily address_family_; // IPv4 or IPv6 (this node's Kademlia network)
412
+ NodeId node_id_; // protected by routing_table_mutex_ for writes; lock-free reads are benign
368
413
  socket_t socket_;
369
414
  std::atomic<bool> running_;
370
-
415
+
416
+ // BEP 42: external-IP-derived node ID.
417
+ // We learn our external address from the top-level "ip" field other nodes echo back in
418
+ // their responses, and vote across distinct responders before trusting it (so a single
419
+ // malicious node cannot poison our node ID). external_address_/voting are guarded by
420
+ // external_ip_mutex_, which is NEVER held while acquiring any other DHT mutex.
421
+ std::string external_address_; // IP currently used to derive node_id_ ("" = random)
422
+ std::unordered_map<std::string, int> external_ip_votes_;// reported external IP -> vote count
423
+ std::unordered_set<std::string> external_ip_voters_; // responder IPs already counted (dedup)
424
+ mutable std::mutex external_ip_mutex_; // Lock order: independent (never nested with others)
425
+ static constexpr int EXTERNAL_IP_VOTE_THRESHOLD = 5; // distinct responders that must agree
426
+ static constexpr size_t MAX_EXTERNAL_IP_VOTERS = 1000; // cap voter set to bound memory
427
+
371
428
  // ============================================================================
372
429
  // MUTEX LOCK ORDER - CRITICAL: Always acquire mutexes in this order to avoid deadlocks
373
430
  // ============================================================================
@@ -507,6 +564,9 @@ private:
507
564
  // Used to decide whether to respond with neighbor_id (spider contact) or real node_id (organic DHT)
508
565
  std::unordered_set<std::string> spider_contacted_ips_;
509
566
 
567
+ // Rate-limit re-bootstrapping in spider_walk to avoid DDOSing bootstrap nodes
568
+ std::chrono::steady_clock::time_point last_spider_bootstrap_{}; // epoch = never bootstrapped
569
+
510
570
  // Spider helper methods
511
571
  void add_spider_node(const DhtNode& node);
512
572
  void add_spider_nodes(const std::vector<DhtNode>& nodes);
@@ -549,6 +609,10 @@ private:
549
609
  int get_bucket_index(const NodeId& id);
550
610
 
551
611
  NodeId generate_node_id();
612
+ // BEP 42 internals
613
+ static bool is_public_address(const std::string& ip);
614
+ void rebuild_routing_table_unlocked(); // re-bucket all nodes after node_id_ changed (routing_table_mutex_ held)
615
+ void maybe_update_external_ip(const std::string& reported_ip, const Peer& responder);
552
616
  NodeId xor_distance(const NodeId& a, const NodeId& b);
553
617
  bool is_closer(const NodeId& a, const NodeId& b, const NodeId& target);
554
618