librats 0.3.1 → 0.5.1
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.
- package/README.md +405 -405
- package/binding.gyp +96 -95
- package/lib/index.d.ts +522 -522
- package/lib/index.js +82 -82
- package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
- package/native-src/CMakeLists.txt +360 -0
- package/native-src/LICENSE +21 -0
- package/native-src/src/bencode.cpp +485 -0
- package/native-src/src/bencode.h +145 -0
- package/native-src/src/bittorrent.cpp +3682 -0
- package/native-src/src/bittorrent.h +731 -0
- package/native-src/src/dht.cpp +2342 -0
- package/native-src/src/dht.h +501 -0
- package/native-src/src/encrypted_socket.cpp +817 -0
- package/native-src/src/encrypted_socket.h +239 -0
- package/native-src/src/file_transfer.cpp +1808 -0
- package/native-src/src/file_transfer.h +567 -0
- package/native-src/src/fs.cpp +639 -0
- package/native-src/src/fs.h +108 -0
- package/native-src/src/gossipsub.cpp +1137 -0
- package/native-src/src/gossipsub.h +403 -0
- package/native-src/src/ice.cpp +1386 -0
- package/native-src/src/ice.h +328 -0
- package/native-src/src/json.hpp +25526 -0
- package/native-src/src/krpc.cpp +558 -0
- package/native-src/src/krpc.h +145 -0
- package/native-src/src/librats.cpp +2715 -0
- package/native-src/src/librats.h +1729 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1317 -0
- package/native-src/src/librats_c.h +237 -0
- package/native-src/src/librats_encryption.cpp +123 -0
- package/native-src/src/librats_file_transfer.cpp +226 -0
- package/native-src/src/librats_gossipsub.cpp +293 -0
- package/native-src/src/librats_ice.cpp +515 -0
- package/native-src/src/librats_logging.cpp +158 -0
- package/native-src/src/librats_mdns.cpp +171 -0
- package/native-src/src/librats_nat.cpp +571 -0
- package/native-src/src/librats_persistence.cpp +815 -0
- package/native-src/src/logger.h +412 -0
- package/native-src/src/mdns.cpp +1178 -0
- package/native-src/src/mdns.h +253 -0
- package/native-src/src/network_utils.cpp +598 -0
- package/native-src/src/network_utils.h +162 -0
- package/native-src/src/noise.cpp +981 -0
- package/native-src/src/noise.h +227 -0
- package/native-src/src/os.cpp +371 -0
- package/native-src/src/os.h +40 -0
- package/native-src/src/rats_export.h +17 -0
- package/native-src/src/sha1.cpp +163 -0
- package/native-src/src/sha1.h +42 -0
- package/native-src/src/socket.cpp +1376 -0
- package/native-src/src/socket.h +309 -0
- package/native-src/src/stun.cpp +484 -0
- package/native-src/src/stun.h +349 -0
- package/native-src/src/threadmanager.cpp +105 -0
- package/native-src/src/threadmanager.h +53 -0
- package/native-src/src/tracker.cpp +1110 -0
- package/native-src/src/tracker.h +268 -0
- package/native-src/src/version.cpp +24 -0
- package/native-src/src/version.h.in +45 -0
- package/native-src/version.rc.in +31 -0
- package/package.json +62 -68
- package/scripts/build-librats.js +241 -194
- package/scripts/postinstall.js +52 -52
- package/scripts/prepare-package.js +187 -91
- package/scripts/verify-installation.js +119 -119
- package/src/librats_node.cpp +1174 -1174
|
@@ -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
|