librats 0.5.2 → 0.5.3

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.
@@ -64,6 +64,15 @@ project(librats VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
64
64
  set(CMAKE_CXX_STANDARD 17)
65
65
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
66
66
 
67
+ # Flags
68
+ # Compiler warnings configuration
69
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
70
+ # additional checks by compiler to found errors ealy on
71
+ add_compile_options(-Wall -Wextra -Wpedantic)
72
+ # not show unused stuff, useful for development
73
+ add_compile_options(-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable)
74
+ endif()
75
+
67
76
  # Options
68
77
  option(RATS_BUILD_EXAMPLES "Build examples" ON)
69
78
  option(RATS_BUILD_TESTS "Build unit tests" ON)
@@ -457,7 +457,7 @@ void DhtClient::handle_message(const std::vector<uint8_t>& data, const Peer& sen
457
457
  handle_krpc_message(*krpc_message, sender);
458
458
  }
459
459
 
460
- void DhtClient::add_node(const DhtNode& node, bool confirmed) {
460
+ void DhtClient::add_node(const DhtNode& node, bool confirmed, bool no_verify) {
461
461
  std::lock_guard<std::mutex> ping_lock(pending_pings_mutex_);
462
462
  std::lock_guard<std::mutex> lock(routing_table_mutex_);
463
463
 
@@ -515,7 +515,7 @@ void DhtClient::add_node(const DhtNode& node, bool confirmed) {
515
515
  return;
516
516
  }
517
517
 
518
- // All nodes are good - find the "worst" good node for ping verification
518
+ // All nodes are good - find the "worst" good node for ping verification or replacement
519
519
  // Worst = highest RTT among nodes not already being pinged
520
520
  DhtNode* worst = nullptr;
521
521
  for (auto& existing : bucket) {
@@ -531,6 +531,19 @@ void DhtClient::add_node(const DhtNode& node, bool confirmed) {
531
531
  return;
532
532
  }
533
533
 
534
+ // If no_verify is set, directly replace the worst node without ping verification
535
+ if (no_verify) {
536
+ LOG_DHT_DEBUG("Force adding node " << node_id_to_hex(node.id)
537
+ << " - replacing worst node " << node_id_to_hex(worst->id)
538
+ << " (rtt=" << worst->rtt << "ms) without ping verification");
539
+ DhtNode new_node = node;
540
+ if (confirmed) {
541
+ new_node.fail_count = 0;
542
+ }
543
+ *worst = new_node;
544
+ return;
545
+ }
546
+
534
547
  // Initiate ping to worst node - if it doesn't respond, replace with candidate
535
548
  LOG_DHT_DEBUG("All nodes good, pinging worst node " << node_id_to_hex(worst->id)
536
549
  << " (rtt=" << worst->rtt << "ms) to verify");
@@ -557,7 +570,7 @@ std::vector<DhtNode> DhtClient::find_closest_nodes_unlocked(const NodeId& target
557
570
  candidates.reserve(count * 3 + K_BUCKET_SIZE * 2);
558
571
 
559
572
  // Add nodes from ideal bucket
560
- if (target_bucket < routing_table_.size()) {
573
+ if (target_bucket >= 0 && static_cast<size_t>(target_bucket) < routing_table_.size()) {
561
574
  const auto& bucket = routing_table_[target_bucket];
562
575
  candidates.insert(candidates.end(), bucket.begin(), bucket.end());
563
576
  LOG_DHT_DEBUG("Collected " << bucket.size() << " nodes from target bucket " << target_bucket);
@@ -657,11 +670,11 @@ int DhtClient::get_bucket_index(const NodeId& id) {
657
670
  NodeId distance = xor_distance(node_id_, id);
658
671
 
659
672
  // Find the position of the most significant bit
660
- for (int i = 0; i < NODE_ID_SIZE; ++i) {
673
+ for (size_t i = 0; i < NODE_ID_SIZE; ++i) {
661
674
  if (distance[i] != 0) {
662
675
  for (int j = 7; j >= 0; --j) {
663
676
  if (distance[i] & (1 << j)) {
664
- return i * 8 + (7 - j);
677
+ return static_cast<int>(i * 8 + (7 - j));
665
678
  }
666
679
  }
667
680
  }
@@ -705,10 +718,20 @@ void DhtClient::handle_krpc_message(const KrpcMessage& message, const Peer& send
705
718
  void DhtClient::handle_krpc_ping(const KrpcMessage& message, const Peer& sender) {
706
719
  LOG_DHT_DEBUG("Handling KRPC PING from " << node_id_to_hex(message.sender_id) << " at " << sender.ip << ":" << sender.port);
707
720
 
721
+ #ifdef RATS_SEARCH_FEATURES
722
+ // Spider mode: check if ignoring requests
723
+ if (spider_mode_.load() && spider_ignore_.load()) {
724
+ return;
725
+ }
726
+ bool no_verify = spider_mode_.load();
727
+ #else
728
+ bool no_verify = false;
729
+ #endif
730
+
708
731
  // Add sender to routing table
709
732
  KrpcNode krpc_node(message.sender_id, sender.ip, sender.port);
710
733
  DhtNode sender_node = krpc_node_to_dht_node(krpc_node);
711
- add_node(sender_node);
734
+ add_node(sender_node, true, no_verify);
712
735
 
713
736
  // Respond with ping response
714
737
  auto response = KrpcProtocol::create_ping_response(message.transaction_id, node_id_);
@@ -718,10 +741,20 @@ void DhtClient::handle_krpc_ping(const KrpcMessage& message, const Peer& sender)
718
741
  void DhtClient::handle_krpc_find_node(const KrpcMessage& message, const Peer& sender) {
719
742
  LOG_DHT_DEBUG("Handling KRPC FIND_NODE from " << node_id_to_hex(message.sender_id) << " at " << sender.ip << ":" << sender.port);
720
743
 
744
+ #ifdef RATS_SEARCH_FEATURES
745
+ // Spider mode: check if ignoring requests
746
+ if (spider_mode_.load() && spider_ignore_.load()) {
747
+ return;
748
+ }
749
+ bool no_verify = spider_mode_.load();
750
+ #else
751
+ bool no_verify = false;
752
+ #endif
753
+
721
754
  // Add sender to routing table
722
755
  KrpcNode krpc_node(message.sender_id, sender.ip, sender.port);
723
756
  DhtNode sender_node = krpc_node_to_dht_node(krpc_node);
724
- add_node(sender_node);
757
+ add_node(sender_node, true, no_verify);
725
758
 
726
759
  // Find closest nodes
727
760
  auto closest_nodes = find_closest_nodes(message.target_id, K_BUCKET_SIZE);
@@ -735,10 +768,20 @@ void DhtClient::handle_krpc_find_node(const KrpcMessage& message, const Peer& se
735
768
  void DhtClient::handle_krpc_get_peers(const KrpcMessage& message, const Peer& sender) {
736
769
  LOG_DHT_DEBUG("Handling KRPC GET_PEERS from " << node_id_to_hex(message.sender_id) << " at " << sender.ip << ":" << sender.port << " for info_hash " << node_id_to_hex(message.info_hash));
737
770
 
771
+ #ifdef RATS_SEARCH_FEATURES
772
+ // Spider mode: check if ignoring requests
773
+ if (spider_mode_.load() && spider_ignore_.load()) {
774
+ return;
775
+ }
776
+ bool no_verify = spider_mode_.load();
777
+ #else
778
+ bool no_verify = false;
779
+ #endif
780
+
738
781
  // Add sender to routing table
739
782
  KrpcNode krpc_node(message.sender_id, sender.ip, sender.port);
740
783
  DhtNode sender_node = krpc_node_to_dht_node(krpc_node);
741
- add_node(sender_node);
784
+ add_node(sender_node, true, no_verify);
742
785
 
743
786
  // Generate a token for this peer
744
787
  std::string token = generate_token(sender);
@@ -765,6 +808,20 @@ void DhtClient::handle_krpc_get_peers(const KrpcMessage& message, const Peer& se
765
808
  void DhtClient::handle_krpc_announce_peer(const KrpcMessage& message, const Peer& sender) {
766
809
  LOG_DHT_DEBUG("Handling KRPC ANNOUNCE_PEER from " << node_id_to_hex(message.sender_id) << " at " << sender.ip << ":" << sender.port);
767
810
 
811
+ #ifdef RATS_SEARCH_FEATURES
812
+ bool is_spider = spider_mode_.load();
813
+
814
+ // Spider mode: check if ignoring requests (but still process announces for callback)
815
+ // Note: We still want to collect announces even when ignoring other requests
816
+
817
+ // In spider mode, skip token verification for maximum collection
818
+ if (!is_spider && !verify_token(sender, message.token)) {
819
+ LOG_DHT_WARN("Invalid token from " << sender.ip << ":" << sender.port << " for KRPC ANNOUNCE_PEER");
820
+ auto error = KrpcProtocol::create_error(message.transaction_id, KrpcErrorCode::ProtocolError, "Invalid token");
821
+ send_krpc_message(error, sender);
822
+ return;
823
+ }
824
+ #else
768
825
  // Verify token
769
826
  if (!verify_token(sender, message.token)) {
770
827
  LOG_DHT_WARN("Invalid token from " << sender.ip << ":" << sender.port << " for KRPC ANNOUNCE_PEER");
@@ -772,16 +829,37 @@ void DhtClient::handle_krpc_announce_peer(const KrpcMessage& message, const Peer
772
829
  send_krpc_message(error, sender);
773
830
  return;
774
831
  }
832
+ bool is_spider = false;
833
+ #endif
775
834
 
776
835
  // Add sender to routing table
777
836
  KrpcNode krpc_node(message.sender_id, sender.ip, sender.port);
778
837
  DhtNode sender_node = krpc_node_to_dht_node(krpc_node);
779
- add_node(sender_node);
838
+ add_node(sender_node, true, is_spider);
839
+
840
+ // Determine the actual port (BEP 5: implied_port support)
841
+ uint16_t peer_port = message.port;
780
842
 
781
843
  // Store the peer announcement
782
- Peer announcing_peer(sender.ip, message.port);
844
+ Peer announcing_peer(sender.ip, peer_port);
783
845
  store_announced_peer(message.info_hash, announcing_peer);
784
846
 
847
+ #ifdef RATS_SEARCH_FEATURES
848
+ // Spider mode: invoke announce callback (ensured hash - peer definitely has it)
849
+ if (is_spider) {
850
+ SpiderAnnounceCallback callback;
851
+ {
852
+ std::lock_guard<std::mutex> lock(spider_callbacks_mutex_);
853
+ callback = spider_announce_callback_;
854
+ }
855
+ if (callback) {
856
+ LOG_DHT_DEBUG("Spider: invoking announce callback for info_hash " << node_id_to_hex(message.info_hash)
857
+ << " from " << announcing_peer.ip << ":" << announcing_peer.port);
858
+ callback(message.info_hash, announcing_peer);
859
+ }
860
+ }
861
+ #endif
862
+
785
863
  // Respond with acknowledgment
786
864
  auto response = KrpcProtocol::create_announce_peer_response(message.transaction_id, node_id_);
787
865
  send_krpc_message(response, sender);
@@ -793,15 +871,21 @@ void DhtClient::handle_krpc_response(const KrpcMessage& message, const Peer& sen
793
871
  // Check if this is a ping verification response before normal processing
794
872
  handle_ping_verification_response(message.transaction_id, message.response_id, sender);
795
873
 
874
+ #ifdef RATS_SEARCH_FEATURES
875
+ bool no_verify = spider_mode_.load();
876
+ #else
877
+ bool no_verify = false;
878
+ #endif
879
+
796
880
  // Add responder to routing table
797
881
  KrpcNode krpc_node(message.response_id, sender.ip, sender.port);
798
882
  DhtNode sender_node = krpc_node_to_dht_node(krpc_node);
799
- add_node(sender_node);
883
+ add_node(sender_node, true, no_verify);
800
884
 
801
885
  // Add any nodes from the response (these are nodes we heard about, not confirmed)
802
886
  for (const auto& node : message.nodes) {
803
887
  DhtNode dht_node = krpc_node_to_dht_node(node);
804
- add_node(dht_node, false); // Not confirmed - just heard about from another node
888
+ add_node(dht_node, false, no_verify); // Not confirmed - just heard about from another node
805
889
  }
806
890
 
807
891
  // Check if this is a response to a pending search (get_peers with peers)
@@ -1247,9 +1331,9 @@ void DhtClient::refresh_buckets() {
1247
1331
  int byte_index = static_cast<int>(i / 8);
1248
1332
  int bit_index = static_cast<int>(i % 8);
1249
1333
 
1250
- if (byte_index < NODE_ID_SIZE) {
1334
+ if (static_cast<size_t>(byte_index) < NODE_ID_SIZE) {
1251
1335
  // Clear the target bit and higher bits
1252
- for (int j = byte_index; j < NODE_ID_SIZE; ++j) {
1336
+ for (size_t j = static_cast<size_t>(byte_index); j < NODE_ID_SIZE; ++j) {
1253
1337
  random_id[j] = node_id_[j];
1254
1338
  }
1255
1339
 
@@ -2156,6 +2240,25 @@ void DhtClient::handle_ping_verification_response(const std::string& transaction
2156
2240
  if (it != pending_pings_.end()) {
2157
2241
  const auto& verification = it->second;
2158
2242
 
2243
+ // Security check: Verify response comes from the IP we pinged
2244
+ // Normalize IPv6-mapped IPv4 addresses (::ffff:x.x.x.x -> x.x.x.x) for comparison
2245
+ auto normalize_ip = [](const std::string& ip) -> std::string {
2246
+ const std::string ipv4_mapped_prefix = "::ffff:";
2247
+ if (ip.compare(0, ipv4_mapped_prefix.size(), ipv4_mapped_prefix) == 0) {
2248
+ return ip.substr(ipv4_mapped_prefix.size());
2249
+ }
2250
+ return ip;
2251
+ };
2252
+
2253
+ std::string responder_ip_normalized = normalize_ip(responder.ip);
2254
+ std::string expected_ip_normalized = normalize_ip(verification.old_node.peer.ip);
2255
+
2256
+ if (responder_ip_normalized != expected_ip_normalized) {
2257
+ LOG_DHT_WARN("Ping verification response from wrong IP " << responder.ip
2258
+ << " (expected " << verification.old_node.peer.ip << ") - ignoring");
2259
+ return; // Don't remove from pending_pings_, let it timeout naturally
2260
+ }
2261
+
2159
2262
  // BEP 5: We pinged the OLD node to check if it's still alive
2160
2263
  if (responder_id == verification.old_node.id) {
2161
2264
  // Calculate RTT
@@ -2182,9 +2285,19 @@ void DhtClient::handle_ping_verification_response(const std::string& transaction
2182
2285
  }
2183
2286
  // Candidate is discarded (not added to routing table)
2184
2287
  } else {
2185
- LOG_DHT_WARN("Ping verification response from unexpected node " << node_id_to_hex(responder_id)
2186
- << " at " << responder.ip << ":" << responder.port
2187
- << " (expected old node " << node_id_to_hex(verification.old_node.id) << ")");
2288
+ // Different node ID responded from the same IP:port!
2289
+ // This means the old node is gone and this IP now belongs to a different node.
2290
+ // Replace the old node with the candidate.
2291
+ LOG_DHT_DEBUG("Different node " << node_id_to_hex(responder_id)
2292
+ << " responded from " << responder.ip << ":" << responder.port
2293
+ << " (expected old node " << node_id_to_hex(verification.old_node.id)
2294
+ << ") - old node is gone, replacing with candidate "
2295
+ << node_id_to_hex(verification.candidate_node.id));
2296
+
2297
+ // Replace old node with candidate
2298
+ DhtNode candidate = verification.candidate_node;
2299
+ candidate.mark_success(); // The responder just proved it's alive
2300
+ perform_replacement(candidate, verification.old_node, verification.bucket_index);
2188
2301
  }
2189
2302
 
2190
2303
  // Remove tracking entries
@@ -2457,4 +2570,74 @@ void DhtClient::set_data_directory(const std::string& directory) {
2457
2570
  LOG_DHT_DEBUG("Data directory set to: " << data_directory_);
2458
2571
  }
2459
2572
 
2573
+ #ifdef RATS_SEARCH_FEATURES
2574
+ // ============================================================================
2575
+ // SPIDER MODE IMPLEMENTATION
2576
+ // ============================================================================
2577
+
2578
+ void DhtClient::set_spider_mode(bool enable) {
2579
+ spider_mode_.store(enable);
2580
+ if (enable) {
2581
+ LOG_DHT_INFO("Spider mode ENABLED - aggressive node discovery active");
2582
+ } else {
2583
+ LOG_DHT_INFO("Spider mode DISABLED - normal DHT operation");
2584
+ }
2585
+ }
2586
+
2587
+ void DhtClient::set_spider_announce_callback(SpiderAnnounceCallback callback) {
2588
+ std::lock_guard<std::mutex> lock(spider_callbacks_mutex_);
2589
+ spider_announce_callback_ = std::move(callback);
2590
+ LOG_DHT_DEBUG("Spider announce callback set");
2591
+ }
2592
+
2593
+ void DhtClient::set_spider_ignore(bool ignore) {
2594
+ spider_ignore_.store(ignore);
2595
+ LOG_DHT_DEBUG("Spider ignore mode set to " << (ignore ? "true" : "false"));
2596
+ }
2597
+
2598
+ void DhtClient::spider_walk() {
2599
+ if (!running_) {
2600
+ return;
2601
+ }
2602
+
2603
+ // Get a random node from the routing table and send find_node
2604
+ DhtNode target_node;
2605
+ bool found = false;
2606
+
2607
+ {
2608
+ std::lock_guard<std::mutex> lock(routing_table_mutex_);
2609
+
2610
+ // Collect all nodes from non-empty buckets
2611
+ std::vector<DhtNode*> all_nodes;
2612
+ for (auto& bucket : routing_table_) {
2613
+ for (auto& node : bucket) {
2614
+ all_nodes.push_back(&node);
2615
+ }
2616
+ }
2617
+
2618
+ if (!all_nodes.empty()) {
2619
+ // Pick a random node
2620
+ static std::random_device rd;
2621
+ static std::mt19937 gen(rd());
2622
+ std::uniform_int_distribution<size_t> dis(0, all_nodes.size() - 1);
2623
+ target_node = *all_nodes[dis(gen)];
2624
+ found = true;
2625
+ }
2626
+ }
2627
+
2628
+ if (found) {
2629
+ // Generate a random target ID for find_node (like spider.js does)
2630
+ NodeId random_target = generate_node_id();
2631
+ send_krpc_find_node(target_node.peer, random_target);
2632
+ LOG_DHT_DEBUG("Spider walk: sent find_node to " << target_node.peer.ip << ":" << target_node.peer.port);
2633
+ } else {
2634
+ // No nodes in routing table, bootstrap
2635
+ LOG_DHT_DEBUG("Spider walk: no nodes in routing table, re-bootstrapping");
2636
+ for (const auto& bootstrap : get_default_bootstrap_nodes()) {
2637
+ send_krpc_find_node(bootstrap, node_id_);
2638
+ }
2639
+ }
2640
+ }
2641
+ #endif // RATS_SEARCH_FEATURES
2642
+
2460
2643
  } // namespace librats
@@ -127,6 +127,16 @@ struct DhtNode {
127
127
  */
128
128
  using PeerDiscoveryCallback = std::function<void(const std::vector<Peer>& peers, const InfoHash& info_hash)>;
129
129
 
130
+ #ifdef RATS_SEARCH_FEATURES
131
+ /**
132
+ * Spider mode callback
133
+ * Called when a peer announces they have a torrent (announce_peer request received)
134
+ * @param info_hash The info hash being announced
135
+ * @param peer The peer that is announcing (with the port they specified)
136
+ */
137
+ using SpiderAnnounceCallback = std::function<void(const InfoHash& info_hash, const Peer& peer)>;
138
+ #endif // RATS_SEARCH_FEATURES
139
+
130
140
  /**
131
141
  * Deferred callbacks structure for avoiding deadlock
132
142
  * Callbacks are collected while holding the mutex, then invoked after releasing it
@@ -253,6 +263,54 @@ public:
253
263
  */
254
264
  bool is_running() const { return running_; }
255
265
 
266
+ #ifdef RATS_SEARCH_FEATURES
267
+ // ============================================================================
268
+ // SPIDER MODE - Aggressive node discovery and announce collection
269
+ // ============================================================================
270
+
271
+ /**
272
+ * Enable spider mode
273
+ * In spider mode:
274
+ * - Nodes are added to routing table without ping verification
275
+ * - All announce_peer requests from other peers are collected via callback
276
+ * @param enable true to enable spider mode, false to disable
277
+ */
278
+ void set_spider_mode(bool enable);
279
+
280
+ /**
281
+ * Check if spider mode is enabled
282
+ * @return true if spider mode is enabled
283
+ */
284
+ bool is_spider_mode() const { return spider_mode_; }
285
+
286
+ /**
287
+ * Set callback for announce_peer requests (spider mode)
288
+ * Called when other peers announce they have a torrent
289
+ * @param callback The callback to invoke
290
+ */
291
+ void set_spider_announce_callback(SpiderAnnounceCallback callback);
292
+
293
+ /**
294
+ * Set spider ignore mode - when true, incoming requests are not processed
295
+ * Similar to rate limiting in the original spider implementation
296
+ * @param ignore true to ignore incoming requests, false to process them
297
+ */
298
+ void set_spider_ignore(bool ignore);
299
+
300
+ /**
301
+ * Check if spider ignore mode is enabled
302
+ * @return true if ignoring incoming requests
303
+ */
304
+ bool is_spider_ignoring() const { return spider_ignore_; }
305
+
306
+ /**
307
+ * Trigger a single spider walk iteration
308
+ * Sends find_node to a random node from the routing table
309
+ * Should be called from external loop at desired frequency
310
+ */
311
+ void spider_walk();
312
+ #endif // RATS_SEARCH_FEATURES
313
+
256
314
  /**
257
315
  * Get default BitTorrent DHT bootstrap nodes
258
316
  * @return Vector of bootstrap nodes
@@ -400,6 +458,14 @@ private:
400
458
  std::condition_variable shutdown_cv_;
401
459
  std::mutex shutdown_mutex_; // Lock order: 7 (can be locked independently)
402
460
 
461
+ #ifdef RATS_SEARCH_FEATURES
462
+ // Spider mode state
463
+ std::atomic<bool> spider_mode_{false};
464
+ std::atomic<bool> spider_ignore_{false}; // When true, ignore incoming requests
465
+ SpiderAnnounceCallback spider_announce_callback_;
466
+ std::mutex spider_callbacks_mutex_; // Protects spider callbacks
467
+ #endif // RATS_SEARCH_FEATURES
468
+
403
469
  // Helper functions
404
470
  void network_loop();
405
471
  void maintenance_loop();
@@ -423,7 +489,7 @@ private:
423
489
  void send_krpc_get_peers(const Peer& peer, const InfoHash& info_hash);
424
490
  void send_krpc_announce_peer(const Peer& peer, const InfoHash& info_hash, uint16_t port, const std::string& token);
425
491
 
426
- void add_node(const DhtNode& node, bool confirmed = true);
492
+ void add_node(const DhtNode& node, bool confirmed = true, bool no_verify = false);
427
493
  std::vector<DhtNode> find_closest_nodes(const NodeId& target, size_t count = K_BUCKET_SIZE);
428
494
  std::vector<DhtNode> find_closest_nodes_unlocked(const NodeId& target, size_t count = K_BUCKET_SIZE);
429
495
  int get_bucket_index(const NodeId& id);
@@ -477,8 +477,8 @@ StunAddress NatTypeDetector::get_mapped_address_different_port(const std::string
477
477
 
478
478
  IceAgent::IceAgent(IceRole role, const IceConfig& config)
479
479
  : role_(role), config_(config), running_(false), state_(IceConnectionState::NEW),
480
- udp_socket_(INVALID_SOCKET_VALUE), tcp_socket_(INVALID_SOCKET_VALUE),
481
- selected_pair_(IceCandidate(), IceCandidate()) {
480
+ selected_pair_(IceCandidate(), IceCandidate()),
481
+ udp_socket_(INVALID_SOCKET_VALUE), tcp_socket_(INVALID_SOCKET_VALUE) {
482
482
 
483
483
  // Initialize NAT detector
484
484
  nat_detector_ = std::make_unique<NatTypeDetector>();
@@ -52,15 +52,15 @@ RatsClient::RatsClient(int listen_port, int max_peers, const NatTraversalConfig&
52
52
  : listen_port_(listen_port),
53
53
  bind_address_(bind_address),
54
54
  max_peers_(max_peers),
55
- nat_config_(nat_config),
56
55
  server_socket_(INVALID_SOCKET_VALUE),
57
56
  running_(false),
58
- auto_discovery_running_(false),
57
+ nat_config_(nat_config),
58
+ data_directory_("."),
59
59
  encryption_enabled_(false),
60
60
  detected_nat_type_(NatType::UNKNOWN),
61
- data_directory_("."),
62
61
  custom_protocol_name_("rats"),
63
- custom_protocol_version_("1.0") {
62
+ custom_protocol_version_("1.0"),
63
+ auto_discovery_running_(false) {
64
64
  // Initialize STUN client
65
65
  stun_client_ = std::make_unique<StunClient>();
66
66
 
@@ -45,6 +45,11 @@ bool RatsClient::start_mdns_discovery(const std::string& service_instance_name,
45
45
  // Create mDNS client
46
46
  mdns_client_ = std::make_unique<MdnsClient>(instance_name, listen_port_);
47
47
 
48
+ // Set service type based on protocol name (e.g., "_rats-search._tcp.local.")
49
+ std::string protocol_name = get_protocol_name();
50
+ std::string service_type = "_" + protocol_name + "._tcp.local.";
51
+ mdns_client_->set_service_type(service_type);
52
+
48
53
  // Set service discovery callback
49
54
  mdns_client_->set_service_callback([this](const MdnsService& service, bool is_new) {
50
55
  handle_mdns_service_discovery(service, is_new);
@@ -25,6 +25,7 @@ namespace librats {
25
25
  MdnsClient::MdnsClient(const std::string& service_instance_name, uint16_t service_port)
26
26
  : service_instance_name_(service_instance_name),
27
27
  service_port_(service_port),
28
+ service_type_(LIBRATS_SERVICE_TYPE), // Default service type
28
29
  multicast_socket_(INVALID_SOCKET_VALUE),
29
30
  running_(false),
30
31
  announcing_(false),
@@ -279,6 +280,11 @@ void MdnsClient::set_query_interval(std::chrono::seconds interval) {
279
280
  query_interval_ = interval;
280
281
  }
281
282
 
283
+ void MdnsClient::set_service_type(const std::string& service_type) {
284
+ service_type_ = service_type;
285
+ LOG_MDNS_INFO("Service type set to: " << service_type_);
286
+ }
287
+
282
288
  bool MdnsClient::create_multicast_socket() {
283
289
  multicast_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
284
290
  if (!librats::is_valid_socket(multicast_socket_)) {
@@ -540,7 +546,7 @@ void MdnsClient::process_query(const DnsMessage& query, const std::string& sende
540
546
  bool should_respond = false;
541
547
 
542
548
  // Check if the question is asking for our service type
543
- if (question.name == LIBRATS_SERVICE_TYPE && question.type == DnsRecordType::PTR) {
549
+ if (question.name == service_type_ && question.type == DnsRecordType::PTR) {
544
550
  should_respond = true;
545
551
  }
546
552
 
@@ -627,7 +633,7 @@ void MdnsClient::extract_service_from_response(const DnsMessage& response, const
627
633
  }
628
634
 
629
635
  bool MdnsClient::is_librats_service(const std::string& service_name) const {
630
- return service_name == LIBRATS_SERVICE_TYPE;
636
+ return service_name == service_type_;
631
637
  }
632
638
 
633
639
  void MdnsClient::add_or_update_service(const MdnsService& service) {
@@ -668,8 +674,8 @@ DnsMessage MdnsClient::create_query_message() {
668
674
  query.header.flags = static_cast<uint16_t>(MdnsFlags::QUERY);
669
675
  query.header.question_count = 1;
670
676
 
671
- // Add question for librats services
672
- DnsQuestion question(LIBRATS_SERVICE_TYPE, DnsRecordType::PTR, DnsRecordClass::CLASS_IN);
677
+ // Add question for services with our service type
678
+ DnsQuestion question(service_type_, DnsRecordType::PTR, DnsRecordClass::CLASS_IN);
673
679
  query.questions.push_back(question);
674
680
 
675
681
  return query;
@@ -689,7 +695,7 @@ DnsMessage MdnsClient::create_announcement_message() {
689
695
  }
690
696
 
691
697
  // Create PTR record
692
- DnsResourceRecord ptr_record = create_ptr_record(LIBRATS_SERVICE_TYPE, our_service_name);
698
+ DnsResourceRecord ptr_record = create_ptr_record(service_type_, our_service_name);
693
699
  announcement.answers.push_back(ptr_record);
694
700
 
695
701
  // Create SRV record
@@ -1120,11 +1126,11 @@ std::string MdnsClient::create_service_instance_name(const std::string& instance
1120
1126
  clean_name = "librats-node";
1121
1127
  }
1122
1128
 
1123
- return clean_name + "." + LIBRATS_SERVICE_TYPE;
1129
+ return clean_name + "." + service_type_;
1124
1130
  }
1125
1131
 
1126
1132
  std::string MdnsClient::extract_instance_name_from_service(const std::string& service_name) {
1127
- size_t pos = service_name.find("." + LIBRATS_SERVICE_TYPE);
1133
+ size_t pos = service_name.find("." + service_type_);
1128
1134
  if (pos != std::string::npos) {
1129
1135
  return service_name.substr(0, pos);
1130
1136
  }
@@ -152,11 +152,13 @@ public:
152
152
  // Configuration
153
153
  void set_announcement_interval(std::chrono::seconds interval);
154
154
  void set_query_interval(std::chrono::seconds interval);
155
+ void set_service_type(const std::string& service_type);
155
156
 
156
157
  private:
157
158
  // Core properties
158
159
  std::string service_instance_name_;
159
160
  uint16_t service_port_;
161
+ std::string service_type_; // Dynamic service type (e.g., "_rats-search._tcp.local.")
160
162
  std::map<std::string, std::string> txt_records_;
161
163
 
162
164
  // Network properties
@@ -10,7 +10,9 @@
10
10
  #ifdef _WIN32
11
11
  #include <windows.h>
12
12
  #include <bcrypt.h>
13
- #pragma comment(lib, "bcrypt.lib")
13
+ #ifdef _MSC_VER
14
+ #pragma comment(lib, "bcrypt.lib")
15
+ #endif
14
16
  #else
15
17
  #include <fcntl.h>
16
18
  #include <unistd.h>
@@ -11,7 +11,9 @@
11
11
  #include <sysinfoapi.h>
12
12
  #include <versionhelpers.h>
13
13
  #include <intrin.h>
14
- #pragma comment(lib, "version.lib")
14
+ #ifdef _MSC_VER
15
+ #pragma comment(lib, "version.lib")
16
+ #endif
15
17
  #elif __APPLE__
16
18
  #include <sys/utsname.h>
17
19
  #include <sys/sysctl.h>
@@ -8,7 +8,9 @@
8
8
  #ifdef _WIN32
9
9
  #include <winsock2.h>
10
10
  #include <ws2tcpip.h>
11
- #pragma comment(lib, "ws2_32.lib")
11
+ #ifdef _MSC_VER
12
+ #pragma comment(lib, "ws2_32.lib")
13
+ #endif
12
14
  typedef SOCKET socket_t;
13
15
  #define INVALID_SOCKET_VALUE INVALID_SOCKET
14
16
  #define SOCKET_ERROR_VALUE SOCKET_ERROR
@@ -106,7 +106,7 @@ std::vector<uint8_t> StunClient::create_binding_request() {
106
106
  std::mt19937 gen(rd());
107
107
  std::uniform_int_distribution<> dis(0, 255);
108
108
 
109
- for (int i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
109
+ for (size_t i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
110
110
  request[8 + i] = static_cast<uint8_t>(dis(gen));
111
111
  }
112
112
 
@@ -226,7 +226,7 @@ void StunClient::generate_transaction_id(uint8_t* transaction_id) {
226
226
  std::mt19937 gen(rd());
227
227
  std::uniform_int_distribution<> dis(0, 255);
228
228
 
229
- for (int i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
229
+ for (size_t i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
230
230
  transaction_id[i] = static_cast<uint8_t>(dis(gen));
231
231
  }
232
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "librats",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Node.js bindings for librats - A high-performance peer-to-peer networking library",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",