librats 0.5.0 → 0.5.2

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 (66) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +1 -0
  3. package/lib/index.d.ts +2 -1
  4. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  5. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  6. package/native-src/CMakeLists.txt +360 -0
  7. package/native-src/LICENSE +21 -0
  8. package/native-src/src/bencode.cpp +485 -0
  9. package/native-src/src/bencode.h +145 -0
  10. package/native-src/src/bittorrent.cpp +3682 -0
  11. package/native-src/src/bittorrent.h +731 -0
  12. package/native-src/src/dht.cpp +2460 -0
  13. package/native-src/src/dht.h +508 -0
  14. package/native-src/src/encrypted_socket.cpp +817 -0
  15. package/native-src/src/encrypted_socket.h +239 -0
  16. package/native-src/src/file_transfer.cpp +1808 -0
  17. package/native-src/src/file_transfer.h +567 -0
  18. package/native-src/src/fs.cpp +639 -0
  19. package/native-src/src/fs.h +108 -0
  20. package/native-src/src/gossipsub.cpp +1137 -0
  21. package/native-src/src/gossipsub.h +403 -0
  22. package/native-src/src/ice.cpp +1386 -0
  23. package/native-src/src/ice.h +328 -0
  24. package/native-src/src/json.hpp +25526 -0
  25. package/native-src/src/krpc.cpp +558 -0
  26. package/native-src/src/krpc.h +145 -0
  27. package/native-src/src/librats.cpp +2735 -0
  28. package/native-src/src/librats.h +1732 -0
  29. package/native-src/src/librats_bittorrent.cpp +167 -0
  30. package/native-src/src/librats_c.cpp +1333 -0
  31. package/native-src/src/librats_c.h +239 -0
  32. package/native-src/src/librats_encryption.cpp +123 -0
  33. package/native-src/src/librats_file_transfer.cpp +226 -0
  34. package/native-src/src/librats_gossipsub.cpp +293 -0
  35. package/native-src/src/librats_ice.cpp +515 -0
  36. package/native-src/src/librats_logging.cpp +158 -0
  37. package/native-src/src/librats_mdns.cpp +171 -0
  38. package/native-src/src/librats_nat.cpp +571 -0
  39. package/native-src/src/librats_persistence.cpp +815 -0
  40. package/native-src/src/logger.h +412 -0
  41. package/native-src/src/mdns.cpp +1178 -0
  42. package/native-src/src/mdns.h +253 -0
  43. package/native-src/src/network_utils.cpp +598 -0
  44. package/native-src/src/network_utils.h +162 -0
  45. package/native-src/src/noise.cpp +981 -0
  46. package/native-src/src/noise.h +227 -0
  47. package/native-src/src/os.cpp +371 -0
  48. package/native-src/src/os.h +40 -0
  49. package/native-src/src/rats_export.h +17 -0
  50. package/native-src/src/sha1.cpp +163 -0
  51. package/native-src/src/sha1.h +42 -0
  52. package/native-src/src/socket.cpp +1376 -0
  53. package/native-src/src/socket.h +309 -0
  54. package/native-src/src/stun.cpp +484 -0
  55. package/native-src/src/stun.h +349 -0
  56. package/native-src/src/threadmanager.cpp +105 -0
  57. package/native-src/src/threadmanager.h +53 -0
  58. package/native-src/src/tracker.cpp +1110 -0
  59. package/native-src/src/tracker.h +268 -0
  60. package/native-src/src/version.cpp +24 -0
  61. package/native-src/src/version.h.in +45 -0
  62. package/native-src/version.rc.in +31 -0
  63. package/package.json +2 -8
  64. package/scripts/build-librats.js +59 -12
  65. package/scripts/prepare-package.js +133 -37
  66. package/src/librats_node.cpp +46 -1
@@ -0,0 +1,571 @@
1
+ #include "librats.h"
2
+ #include "stun.h"
3
+ #include "network_utils.h"
4
+ #include <algorithm>
5
+ #include <random>
6
+
7
+ // Logging macros for NAT operations
8
+ #ifdef TESTING
9
+ #define LOG_NAT_DEBUG(message) LOG_DEBUG("nat", "[pointer: " << this << "] " << message)
10
+ #define LOG_NAT_INFO(message) LOG_INFO("nat", "[pointer: " << this << "] " << message)
11
+ #define LOG_NAT_WARN(message) LOG_WARN("nat", "[pointer: " << this << "] " << message)
12
+ #define LOG_NAT_ERROR(message) LOG_ERROR("nat", "[pointer: " << this << "] " << message)
13
+ #else
14
+ #define LOG_NAT_DEBUG(message) LOG_DEBUG("nat", message)
15
+ #define LOG_NAT_INFO(message) LOG_INFO("nat", message)
16
+ #define LOG_NAT_WARN(message) LOG_WARN("nat", message)
17
+ #define LOG_NAT_ERROR(message) LOG_ERROR("nat", message)
18
+ #endif
19
+
20
+ namespace librats {
21
+
22
+ //=============================================================================
23
+ // NAT Traversal Initialization and Detection
24
+ //=============================================================================
25
+
26
+ void RatsClient::initialize_nat_traversal() {
27
+ LOG_NAT_INFO("Initializing NAT traversal capabilities");
28
+
29
+ // Detect and cache NAT type in background thread
30
+ add_managed_thread(std::thread([this]() {
31
+ detect_and_cache_nat_type();
32
+ }), "nat-detection");
33
+
34
+ // Initialize ICE if enabled
35
+ if (ice_agent_) {
36
+ LOG_NAT_INFO("Starting ICE agent for NAT traversal");
37
+ if (!ice_agent_->start()) {
38
+ LOG_NAT_ERROR("Failed to start ICE agent");
39
+ } else {
40
+ LOG_NAT_INFO("ICE agent started successfully");
41
+ }
42
+ }
43
+ }
44
+
45
+ void RatsClient::detect_and_cache_nat_type() {
46
+ if (!nat_detector_) {
47
+ LOG_NAT_ERROR("NAT detector not initialized");
48
+ return;
49
+ }
50
+
51
+ // Check if client is still running before proceeding
52
+ if (!running_.load()) {
53
+ LOG_NAT_DEBUG("Client not running, skipping NAT detection");
54
+ return;
55
+ }
56
+
57
+ LOG_NAT_INFO("Detecting NAT type and characteristics");
58
+
59
+ try {
60
+ // Use STUN servers from configuration
61
+ std::vector<std::string> stun_servers = nat_config_.stun_servers;
62
+ if (stun_servers.empty()) {
63
+ stun_servers.push_back("stun.l.google.com:19302");
64
+ stun_servers.push_back("stun1.l.google.com:19302");
65
+ }
66
+
67
+ // Check again before mutex access
68
+ if (!running_.load()) {
69
+ LOG_NAT_DEBUG("Client stopped during NAT detection setup");
70
+ return;
71
+ }
72
+
73
+ // Detect detailed NAT characteristics
74
+ {
75
+ std::lock_guard<std::mutex> lock(nat_mutex_);
76
+ nat_characteristics_ = nat_detector_->detect_nat_characteristics(stun_servers, 5000);
77
+
78
+ // Map detailed characteristics to simple NAT type
79
+ detected_nat_type_ = map_characteristics_to_nat_type(nat_characteristics_);
80
+ }
81
+
82
+ log_nat_detection_results();
83
+
84
+ } catch (const std::exception& e) {
85
+ LOG_NAT_ERROR("NAT detection failed: " << e.what());
86
+ // Only try to update state if client is still running
87
+ if (running_.load()) {
88
+ std::lock_guard<std::mutex> lock(nat_mutex_);
89
+ detected_nat_type_ = NatType::UNKNOWN;
90
+ }
91
+ }
92
+ }
93
+
94
+ NatType RatsClient::map_characteristics_to_nat_type(const NatTypeInfo& characteristics) {
95
+ if (!characteristics.has_nat) {
96
+ return NatType::OPEN_INTERNET;
97
+ } else if (characteristics.mapping_behavior == NatBehavior::ENDPOINT_INDEPENDENT &&
98
+ characteristics.filtering_behavior == NatBehavior::ENDPOINT_INDEPENDENT) {
99
+ return NatType::FULL_CONE;
100
+ } else if (characteristics.mapping_behavior == NatBehavior::ENDPOINT_INDEPENDENT &&
101
+ characteristics.filtering_behavior == NatBehavior::ADDRESS_DEPENDENT) {
102
+ return NatType::RESTRICTED_CONE;
103
+ } else if (characteristics.mapping_behavior == NatBehavior::ENDPOINT_INDEPENDENT &&
104
+ characteristics.filtering_behavior == NatBehavior::ADDRESS_PORT_DEPENDENT) {
105
+ return NatType::PORT_RESTRICTED;
106
+ } else if (characteristics.mapping_behavior == NatBehavior::ADDRESS_PORT_DEPENDENT) {
107
+ return NatType::SYMMETRIC;
108
+ } else {
109
+ return NatType::UNKNOWN;
110
+ }
111
+ }
112
+
113
+ void RatsClient::log_nat_detection_results() {
114
+ LOG_NAT_INFO("NAT detection completed:");
115
+ LOG_NAT_INFO(" Type: " << static_cast<int>(detected_nat_type_));
116
+ LOG_NAT_INFO(" Has NAT: " << nat_characteristics_.has_nat);
117
+ LOG_NAT_INFO(" Filtering: " << static_cast<int>(nat_characteristics_.filtering_behavior));
118
+ LOG_NAT_INFO(" Mapping: " << static_cast<int>(nat_characteristics_.mapping_behavior));
119
+ LOG_NAT_INFO(" Port Preservation: " << nat_characteristics_.preserves_port);
120
+ LOG_NAT_INFO(" Hairpin Support: " << nat_characteristics_.hairpin_support);
121
+ }
122
+
123
+ NatType RatsClient::detect_nat_type() {
124
+ std::lock_guard<std::mutex> lock(nat_mutex_);
125
+ return detected_nat_type_;
126
+ }
127
+
128
+ NatTypeInfo RatsClient::get_nat_characteristics() {
129
+ std::lock_guard<std::mutex> lock(nat_mutex_);
130
+ return nat_characteristics_;
131
+ }
132
+
133
+ //=============================================================================
134
+ // STUN and Public IP Discovery
135
+ //=============================================================================
136
+
137
+ bool RatsClient::discover_and_ignore_public_ip(const std::string& stun_server, int stun_port) {
138
+ if (!stun_client_) {
139
+ LOG_NAT_ERROR("STUN client not initialized");
140
+ return false;
141
+ }
142
+
143
+ LOG_NAT_INFO("Discovering public IP address using STUN server: " << stun_server << ":" << stun_port);
144
+
145
+ StunAddress public_address;
146
+ if (!stun_client_->get_public_address(stun_server, stun_port, public_address)) {
147
+ LOG_NAT_ERROR("Failed to discover public IP address via STUN");
148
+ return false;
149
+ }
150
+
151
+ // Store the discovered public IP
152
+ {
153
+ std::lock_guard<std::mutex> lock(public_ip_mutex_);
154
+ public_ip_ = public_address.ip;
155
+ }
156
+
157
+ LOG_NAT_INFO("Discovered public IP address: " << public_address.ip << " (port: " << public_address.port << ")");
158
+
159
+ // Add to ignore list
160
+ add_ignored_address(public_address.ip);
161
+
162
+ LOG_NAT_INFO("Added public IP " << public_address.ip << " to ignore list");
163
+ return true;
164
+ }
165
+
166
+ std::string RatsClient::get_public_ip() const {
167
+ std::lock_guard<std::mutex> lock(public_ip_mutex_);
168
+ return public_ip_;
169
+ }
170
+
171
+ //=============================================================================
172
+ // Connection Strategy Selection
173
+ //=============================================================================
174
+
175
+ std::string RatsClient::select_best_connection_strategy(const std::string& host, int port) {
176
+ NatType local_nat = detect_nat_type();
177
+
178
+ // Strategy selection based on NAT type
179
+ switch (local_nat) {
180
+ case NatType::OPEN_INTERNET:
181
+ return "direct";
182
+
183
+ case NatType::FULL_CONE:
184
+ case NatType::RESTRICTED_CONE:
185
+ if (nat_config_.enable_ice && ice_agent_) {
186
+ return "ice";
187
+ } else {
188
+ return "stun";
189
+ }
190
+
191
+ case NatType::PORT_RESTRICTED:
192
+ if (nat_config_.enable_ice && ice_agent_) {
193
+ return "ice";
194
+ } else if (nat_config_.enable_hole_punching) {
195
+ return "hole_punch";
196
+ } else {
197
+ return "stun";
198
+ }
199
+
200
+ case NatType::SYMMETRIC:
201
+ if (nat_config_.enable_turn_relay) {
202
+ return "turn";
203
+ } else if (nat_config_.enable_ice && ice_agent_) {
204
+ return "ice";
205
+ } else {
206
+ return "hole_punch";
207
+ }
208
+
209
+ default:
210
+ // Unknown NAT type - try ICE if available, otherwise STUN
211
+ if (nat_config_.enable_ice && ice_agent_) {
212
+ return "ice";
213
+ } else {
214
+ return "stun";
215
+ }
216
+ }
217
+ }
218
+
219
+ //=============================================================================
220
+ // Connection Attempt Methods
221
+ //=============================================================================
222
+
223
+ bool RatsClient::attempt_direct_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
224
+ LOG_NAT_DEBUG("Attempting direct connection to " << host << ":" << port);
225
+
226
+ // Check if this peer should be ignored (local interface)
227
+ if (should_ignore_peer(host, port)) {
228
+ result.error_message = "Target is local interface address";
229
+ return false;
230
+ }
231
+
232
+ // Check peer limit
233
+ if (is_peer_limit_reached()) {
234
+ result.error_message = "Peer limit reached";
235
+ return false;
236
+ }
237
+
238
+ // Check if already connected
239
+ std::string peer_address = normalize_peer_address(host, port);
240
+ if (is_already_connected_to_address(peer_address)) {
241
+ result.error_message = "Already connected to this address";
242
+ return true; // Not really an error
243
+ }
244
+
245
+ return perform_tcp_connection(host, port, result);
246
+ }
247
+
248
+ bool RatsClient::attempt_stun_assisted_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
249
+ LOG_NAT_DEBUG("Attempting STUN-assisted connection to " << host << ":" << port);
250
+
251
+ // First ensure we have discovered our public IP
252
+ if (get_public_ip().empty()) {
253
+ if (!discover_and_ignore_public_ip()) {
254
+ result.error_message = "Failed to discover public IP via STUN";
255
+ return false;
256
+ }
257
+ }
258
+
259
+ // For STUN-assisted, we still use direct TCP but with knowledge of public IP
260
+ // The actual NAT traversal happens through the STUN binding discovery
261
+ return attempt_direct_connection(host, port, result);
262
+ }
263
+
264
+ bool RatsClient::attempt_turn_relay_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
265
+ LOG_NAT_DEBUG("Attempting TURN relay connection to " << host << ":" << port);
266
+
267
+ if (nat_config_.turn_servers.empty()) {
268
+ result.error_message = "No TURN servers configured";
269
+ return false;
270
+ }
271
+
272
+ try {
273
+ // For now, fall back to direct connection
274
+ // Full TURN implementation would require:
275
+ // 1. TURN allocation
276
+ // 2. Permission creation
277
+ // 3. Data relay through TURN server
278
+ LOG_NAT_INFO("TURN relay requires coordination - using direct connection as fallback");
279
+
280
+ bool success = attempt_direct_connection(host, port, result);
281
+ if (success) {
282
+ result.method = "turn_fallback";
283
+ }
284
+
285
+ return success;
286
+
287
+ } catch (const std::exception& e) {
288
+ result.error_message = "TURN relay failed: " + std::string(e.what());
289
+ return false;
290
+ }
291
+ }
292
+
293
+ bool RatsClient::attempt_hole_punch_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
294
+ LOG_NAT_DEBUG("Attempting hole punch connection to " << host << ":" << port);
295
+
296
+ if (!nat_config_.enable_hole_punching) {
297
+ result.error_message = "Hole punching disabled in configuration";
298
+ return false;
299
+ }
300
+
301
+ try {
302
+ // Coordinated hole punching would require:
303
+ // 1. Peer coordination through DHT or signaling
304
+ // 2. Synchronized UDP hole punching attempts
305
+ // 3. TCP hole punching (where supported)
306
+ // 4. Connection establishment on successful punch
307
+
308
+ LOG_NAT_INFO("Coordinated hole punching requires peer cooperation - using direct connection");
309
+
310
+ bool success = attempt_direct_connection(host, port, result);
311
+ if (success) {
312
+ result.method = "hole_punch_direct";
313
+ }
314
+
315
+ return success;
316
+
317
+ } catch (const std::exception& e) {
318
+ result.error_message = "Hole punch failed: " + std::string(e.what());
319
+ return false;
320
+ }
321
+ }
322
+
323
+ bool RatsClient::perform_tcp_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
324
+ // Attempt TCP connection with 10-second timeout
325
+ socket_t peer_socket = create_tcp_client(host, port, 10000); // 10 seconds = 10000ms
326
+ if (!is_valid_socket(peer_socket)) {
327
+ result.error_message = "Failed to create TCP connection (connection may have timed out after 10s)";
328
+ return false;
329
+ }
330
+
331
+ // Initialize encryption if enabled
332
+ if (is_encryption_enabled()) {
333
+ if (!encrypted_communication::initialize_outgoing_connection(peer_socket)) {
334
+ result.error_message = "Failed to initialize encryption";
335
+ close_socket(peer_socket);
336
+ return false;
337
+ }
338
+ }
339
+
340
+ // Create peer and add to management
341
+ std::string connection_info = host + ":" + std::to_string(port);
342
+ std::string peer_hash_id = generate_peer_hash_id(peer_socket, connection_info);
343
+ std::string peer_address = normalize_peer_address(host, port);
344
+
345
+ {
346
+ std::lock_guard<std::mutex> lock(peers_mutex_);
347
+ RatsPeer new_peer(peer_hash_id, host, port, peer_socket, peer_address, true);
348
+ new_peer.encryption_enabled = is_encryption_enabled();
349
+ new_peer.connection_method = "direct";
350
+ add_peer_unlocked(new_peer);
351
+ }
352
+
353
+ // Start handling this peer
354
+ add_managed_thread(std::thread(&RatsClient::handle_client, this, peer_socket, peer_hash_id),
355
+ "direct-connect-handler-" + peer_hash_id.substr(0, 8));
356
+
357
+ // Send handshake if not encrypted
358
+ if (!is_encryption_enabled()) {
359
+ if (!send_handshake(peer_socket, get_our_peer_id())) {
360
+ result.error_message = "Failed to send handshake";
361
+ disconnect_peer(peer_socket);
362
+ return false;
363
+ }
364
+ }
365
+
366
+ return true;
367
+ }
368
+
369
+ //=============================================================================
370
+ // Hole Punching Coordination
371
+ //=============================================================================
372
+
373
+ bool RatsClient::coordinate_hole_punching(const std::string& peer_ip, uint16_t peer_port,
374
+ const nlohmann::json& coordination_data) {
375
+ LOG_NAT_INFO("Coordinating hole punching with " << peer_ip << ":" << peer_port);
376
+
377
+ if (!nat_config_.enable_hole_punching) {
378
+ LOG_NAT_ERROR("Hole punching disabled in configuration");
379
+ return false;
380
+ }
381
+
382
+ try {
383
+ std::string method = coordination_data.value("method", "");
384
+ if (method == "udp_hole_punch") {
385
+ // Use ICE agent for UDP hole punching if available
386
+ if (ice_agent_ && ice_agent_->is_running()) {
387
+ return ice_agent_->perform_hole_punching(peer_ip, peer_port);
388
+ }
389
+ }
390
+
391
+ // For now, return success to indicate coordination was received
392
+ LOG_NAT_INFO("Hole punching coordination received but not fully implemented");
393
+ return true;
394
+
395
+ } catch (const std::exception& e) {
396
+ LOG_NAT_ERROR("Hole punching coordination failed: " << e.what());
397
+ return false;
398
+ }
399
+ }
400
+
401
+ //=============================================================================
402
+ // NAT Information Exchange
403
+ //=============================================================================
404
+
405
+ void RatsClient::send_nat_info_to_peer(socket_t socket, const std::string& peer_id) {
406
+ LOG_NAT_DEBUG("Sending NAT info to peer " << peer_id);
407
+
408
+ try {
409
+ nlohmann::json nat_info;
410
+ nat_info["nat_type"] = static_cast<int>(detect_nat_type());
411
+ nat_info["has_nat"] = get_nat_characteristics().has_nat;
412
+ nat_info["public_ip"] = get_public_ip();
413
+ nat_info["filtering_behavior"] = static_cast<int>(get_nat_characteristics().filtering_behavior);
414
+ nat_info["mapping_behavior"] = static_cast<int>(get_nat_characteristics().mapping_behavior);
415
+ nat_info["preserves_port"] = get_nat_characteristics().preserves_port;
416
+ nat_info["hairpin_support"] = get_nat_characteristics().hairpin_support;
417
+
418
+ nlohmann::json nat_message = create_rats_message("nat_info_exchange", nat_info, get_our_peer_id());
419
+
420
+ if (send_json_to_peer(socket, nat_message)) {
421
+ LOG_NAT_DEBUG("Sent NAT info to peer " << peer_id);
422
+ } else {
423
+ LOG_NAT_ERROR("Failed to send NAT info to peer " << peer_id);
424
+ }
425
+
426
+ } catch (const std::exception& e) {
427
+ LOG_NAT_ERROR("Failed to send NAT info: " << e.what());
428
+ }
429
+ }
430
+
431
+ void RatsClient::handle_nat_info_exchange_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
432
+ LOG_NAT_INFO("Received NAT info exchange from peer " << peer_hash_id);
433
+
434
+ try {
435
+ // Extract peer's NAT information
436
+ NatType remote_nat_type = static_cast<NatType>(payload.value("nat_type", static_cast<int>(NatType::UNKNOWN)));
437
+ bool has_nat = payload.value("has_nat", false);
438
+ std::string public_ip = payload.value("public_ip", "");
439
+
440
+ // Update peer with NAT information
441
+ {
442
+ std::lock_guard<std::mutex> lock(peers_mutex_);
443
+ auto socket_it = socket_to_peer_id_.find(socket);
444
+ if (socket_it != socket_to_peer_id_.end()) {
445
+ auto peer_it = peers_.find(socket_it->second);
446
+ if (peer_it != peers_.end()) {
447
+ peer_it->second.detected_nat_type = remote_nat_type;
448
+ }
449
+ }
450
+ }
451
+
452
+ LOG_NAT_INFO("Peer " << peer_hash_id << " NAT info: type=" << static_cast<int>(remote_nat_type)
453
+ << ", has_nat=" << has_nat << ", public_ip=" << public_ip);
454
+
455
+ // Send our NAT information in response if not already sent
456
+ nlohmann::json our_nat_info;
457
+ our_nat_info["nat_type"] = static_cast<int>(detect_nat_type());
458
+ our_nat_info["has_nat"] = get_nat_characteristics().has_nat;
459
+ our_nat_info["public_ip"] = get_public_ip();
460
+ our_nat_info["response"] = true;
461
+
462
+ nlohmann::json nat_message = create_rats_message("nat_info_exchange", our_nat_info, get_our_peer_id());
463
+
464
+ // Only send if this wasn't already a response
465
+ if (!payload.value("response", false)) {
466
+ if (send_json_to_peer(socket, nat_message)) {
467
+ LOG_NAT_DEBUG("Sent NAT info to peer " << peer_hash_id);
468
+ } else {
469
+ LOG_NAT_ERROR("Failed to send NAT info to peer " << peer_hash_id);
470
+ }
471
+ }
472
+
473
+ } catch (const std::exception& e) {
474
+ LOG_NAT_ERROR("Failed to handle NAT info exchange: " << e.what());
475
+ }
476
+ }
477
+
478
+ void RatsClient::handle_hole_punch_coordination_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
479
+ LOG_NAT_INFO("Received hole punch coordination from peer " << peer_hash_id);
480
+
481
+ try {
482
+ std::string method = payload.value("method", "");
483
+ std::string peer_ip = payload.value("peer_ip", "");
484
+ int peer_port = payload.value("peer_port", 0);
485
+
486
+ if (method.empty() || peer_ip.empty() || peer_port <= 0) {
487
+ LOG_NAT_WARN("Invalid hole punch coordination data from peer " << peer_hash_id);
488
+ return;
489
+ }
490
+
491
+ LOG_NAT_INFO("Coordinating " << method << " hole punch with " << peer_ip << ":" << peer_port);
492
+
493
+ // Perform hole punching coordination
494
+ bool success = coordinate_hole_punching(peer_ip, peer_port, payload);
495
+
496
+ // Send coordination response
497
+ nlohmann::json response_payload;
498
+ response_payload["success"] = success;
499
+ response_payload["method"] = method;
500
+ response_payload["timestamp"] = std::chrono::duration_cast<std::chrono::milliseconds>(
501
+ std::chrono::high_resolution_clock::now().time_since_epoch()).count();
502
+
503
+ nlohmann::json response_message = create_rats_message("hole_punch_response", response_payload, get_our_peer_id());
504
+
505
+ if (send_json_to_peer(socket, response_message)) {
506
+ LOG_NAT_DEBUG("Sent hole punch coordination response to peer " << peer_hash_id);
507
+ } else {
508
+ LOG_NAT_ERROR("Failed to send hole punch coordination response to peer " << peer_hash_id);
509
+ }
510
+
511
+ } catch (const std::exception& e) {
512
+ LOG_NAT_ERROR("Failed to handle hole punch coordination: " << e.what());
513
+ }
514
+ }
515
+
516
+ //=============================================================================
517
+ // Statistics and Connection Information
518
+ //=============================================================================
519
+
520
+ nlohmann::json RatsClient::get_nat_traversal_statistics() const {
521
+ nlohmann::json stats;
522
+
523
+ {
524
+ std::lock_guard<std::mutex> lock(nat_mutex_);
525
+ stats["detected_nat_type"] = static_cast<int>(detected_nat_type_);
526
+ stats["has_nat"] = nat_characteristics_.has_nat;
527
+ stats["filtering_behavior"] = static_cast<int>(nat_characteristics_.filtering_behavior);
528
+ stats["mapping_behavior"] = static_cast<int>(nat_characteristics_.mapping_behavior);
529
+ stats["preserves_port"] = nat_characteristics_.preserves_port;
530
+ stats["hairpin_support"] = nat_characteristics_.hairpin_support;
531
+ stats["description"] = nat_characteristics_.description;
532
+ }
533
+
534
+ // ICE statistics
535
+ if (ice_agent_) {
536
+ stats["ice_available"] = true;
537
+ stats["ice_running"] = ice_agent_->is_running();
538
+ stats["ice_state"] = static_cast<int>(ice_agent_->get_connection_state());
539
+
540
+ if (ice_agent_->is_running()) {
541
+ auto local_candidates = ice_agent_->get_local_candidates();
542
+ stats["local_ice_candidates"] = local_candidates.size();
543
+ }
544
+ } else {
545
+ stats["ice_available"] = false;
546
+ }
547
+
548
+ // NAT traversal configuration
549
+ stats["config"] = {
550
+ {"enable_ice", nat_config_.enable_ice},
551
+ {"enable_upnp", nat_config_.enable_upnp},
552
+ {"enable_hole_punching", nat_config_.enable_hole_punching},
553
+ {"enable_turn_relay", nat_config_.enable_turn_relay},
554
+ {"stun_servers", nat_config_.stun_servers},
555
+ {"turn_servers", nat_config_.turn_servers}
556
+ };
557
+
558
+ return stats;
559
+ }
560
+
561
+ void RatsClient::update_connection_statistics(const std::string& peer_id, const ConnectionAttemptResult& result) {
562
+ std::lock_guard<std::mutex> lock(connection_attempts_mutex_);
563
+ connection_attempts_[peer_id].push_back(result);
564
+
565
+ // Keep only the last 10 attempts per peer to avoid memory growth
566
+ if (connection_attempts_[peer_id].size() > 10) {
567
+ connection_attempts_[peer_id].erase(connection_attempts_[peer_id].begin());
568
+ }
569
+ }
570
+
571
+ } // namespace librats