librats 0.5.0 → 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/binding.gyp +1 -0
- 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 +2 -8
- package/scripts/build-librats.js +59 -12
- package/scripts/prepare-package.js +133 -37
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
#include "stun.h"
|
|
2
|
+
#include "ice.h"
|
|
3
|
+
#include "network_utils.h"
|
|
4
|
+
#include "logger.h"
|
|
5
|
+
#include <random>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <chrono>
|
|
8
|
+
|
|
9
|
+
#ifdef _WIN32
|
|
10
|
+
#include <winsock2.h>
|
|
11
|
+
#include <ws2tcpip.h>
|
|
12
|
+
#else
|
|
13
|
+
#include <arpa/inet.h>
|
|
14
|
+
#include <netinet/in.h>
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
// STUN module logging macros
|
|
18
|
+
#define LOG_STUN_DEBUG(message) LOG_DEBUG("stun", message)
|
|
19
|
+
#define LOG_STUN_INFO(message) LOG_INFO("stun", message)
|
|
20
|
+
#define LOG_STUN_WARN(message) LOG_WARN("stun", message)
|
|
21
|
+
#define LOG_STUN_ERROR(message) LOG_ERROR("stun", message)
|
|
22
|
+
|
|
23
|
+
namespace librats {
|
|
24
|
+
|
|
25
|
+
StunClient::StunClient() {
|
|
26
|
+
LOG_STUN_DEBUG("STUN client created");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
StunClient::~StunClient() {
|
|
30
|
+
LOG_STUN_DEBUG("STUN client destroyed");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool StunClient::get_public_address_from_google(StunAddress& public_address, int timeout_ms) {
|
|
34
|
+
return get_public_address("stun.l.google.com", 19302, public_address, timeout_ms);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool StunClient::get_public_address(const std::string& stun_server,
|
|
38
|
+
int stun_port,
|
|
39
|
+
StunAddress& public_address,
|
|
40
|
+
int timeout_ms) {
|
|
41
|
+
LOG_STUN_INFO("Getting public address from STUN server: " << stun_server << ":" << stun_port);
|
|
42
|
+
|
|
43
|
+
// Initialize socket library (safe to call multiple times)
|
|
44
|
+
if (!init_socket_library()) {
|
|
45
|
+
LOG_STUN_ERROR("Failed to initialize socket library");
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create UDP socket for STUN
|
|
50
|
+
socket_t stun_socket = create_udp_socket_v4(0); // Use ephemeral port
|
|
51
|
+
if (!is_valid_socket(stun_socket)) {
|
|
52
|
+
LOG_STUN_ERROR("Failed to create UDP socket for STUN");
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Set socket to non-blocking for timeout support
|
|
57
|
+
if (!set_socket_nonblocking(stun_socket)) {
|
|
58
|
+
LOG_STUN_WARN("Failed to set STUN socket to non-blocking mode");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Create STUN binding request
|
|
62
|
+
std::vector<uint8_t> request = create_binding_request();
|
|
63
|
+
|
|
64
|
+
// Send request to STUN server
|
|
65
|
+
if (!send_stun_request(stun_socket, stun_server, stun_port, request)) {
|
|
66
|
+
LOG_STUN_ERROR("Failed to send STUN request to " << stun_server << ":" << stun_port);
|
|
67
|
+
close_socket(stun_socket);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Receive response
|
|
72
|
+
std::vector<uint8_t> response;
|
|
73
|
+
if (!receive_stun_response(stun_socket, response, timeout_ms)) {
|
|
74
|
+
LOG_STUN_ERROR("Failed to receive STUN response from " << stun_server << ":" << stun_port);
|
|
75
|
+
close_socket(stun_socket);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Parse response to get mapped address
|
|
80
|
+
if (!parse_binding_response(response, public_address)) {
|
|
81
|
+
LOG_STUN_ERROR("Failed to parse STUN response from " << stun_server << ":" << stun_port);
|
|
82
|
+
close_socket(stun_socket);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
close_socket(stun_socket);
|
|
87
|
+
|
|
88
|
+
LOG_STUN_INFO("Successfully got public address: " << public_address.ip << ":" << public_address.port);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
std::vector<uint8_t> StunClient::create_binding_request() {
|
|
93
|
+
std::vector<uint8_t> request(stun::HEADER_SIZE);
|
|
94
|
+
|
|
95
|
+
// Message Type: Binding Request
|
|
96
|
+
write_uint16(request.data(), stun::BINDING_REQUEST);
|
|
97
|
+
|
|
98
|
+
// Message Length: 0 (no attributes for basic binding request)
|
|
99
|
+
write_uint16(request.data() + 2, 0);
|
|
100
|
+
|
|
101
|
+
// Magic Cookie
|
|
102
|
+
write_uint32(request.data() + 4, stun::MAGIC_COOKIE);
|
|
103
|
+
|
|
104
|
+
// Transaction ID (96 bits = 12 bytes)
|
|
105
|
+
std::random_device rd;
|
|
106
|
+
std::mt19937 gen(rd());
|
|
107
|
+
std::uniform_int_distribution<> dis(0, 255);
|
|
108
|
+
|
|
109
|
+
for (int i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
|
|
110
|
+
request[8 + i] = static_cast<uint8_t>(dis(gen));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
LOG_STUN_DEBUG("Created STUN binding request (" << request.size() << " bytes)");
|
|
114
|
+
return request;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
bool StunClient::parse_binding_response(const std::vector<uint8_t>& response,
|
|
118
|
+
StunAddress& mapped_address) {
|
|
119
|
+
if (response.size() < stun::HEADER_SIZE) {
|
|
120
|
+
LOG_STUN_ERROR("STUN response too short: " << response.size() << " bytes");
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Parse header
|
|
125
|
+
uint16_t message_type = parse_uint16(response.data());
|
|
126
|
+
uint16_t message_length = parse_uint16(response.data() + 2);
|
|
127
|
+
uint32_t magic_cookie = parse_uint32(response.data() + 4);
|
|
128
|
+
|
|
129
|
+
// Verify this is a binding response
|
|
130
|
+
if (message_type != stun::BINDING_RESPONSE) {
|
|
131
|
+
LOG_STUN_ERROR("Invalid STUN message type: 0x" << std::hex << message_type);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Verify magic cookie
|
|
136
|
+
if (magic_cookie != stun::MAGIC_COOKIE) {
|
|
137
|
+
LOG_STUN_ERROR("Invalid STUN magic cookie: 0x" << std::hex << magic_cookie);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if response has the expected length
|
|
142
|
+
if (response.size() != stun::HEADER_SIZE + message_length) {
|
|
143
|
+
LOG_STUN_ERROR("STUN response length mismatch: expected " <<
|
|
144
|
+
(stun::HEADER_SIZE + message_length) << ", got " << response.size());
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Parse attributes
|
|
149
|
+
size_t offset = stun::HEADER_SIZE;
|
|
150
|
+
bool found_mapped_address = false;
|
|
151
|
+
|
|
152
|
+
while (offset < response.size()) {
|
|
153
|
+
if (offset + 4 > response.size()) {
|
|
154
|
+
LOG_STUN_ERROR("Truncated STUN attribute header");
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
uint16_t attr_type = parse_uint16(response.data() + offset);
|
|
159
|
+
uint16_t attr_length = parse_uint16(response.data() + offset + 2);
|
|
160
|
+
offset += 4;
|
|
161
|
+
|
|
162
|
+
if (offset + attr_length > response.size()) {
|
|
163
|
+
LOG_STUN_ERROR("Truncated STUN attribute value");
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Handle XOR-MAPPED-ADDRESS (preferred) or MAPPED-ADDRESS
|
|
168
|
+
if (attr_type == stun::ATTR_XOR_MAPPED_ADDRESS || attr_type == stun::ATTR_MAPPED_ADDRESS) {
|
|
169
|
+
if (attr_length < 8) {
|
|
170
|
+
LOG_STUN_ERROR("Invalid STUN address attribute length: " << attr_length);
|
|
171
|
+
offset += attr_length;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const uint8_t* attr_data = response.data() + offset;
|
|
176
|
+
|
|
177
|
+
// Skip reserved byte
|
|
178
|
+
uint8_t family = attr_data[1];
|
|
179
|
+
uint16_t port = parse_uint16(attr_data + 2);
|
|
180
|
+
|
|
181
|
+
if (family == stun::FAMILY_IPV4 && attr_length >= 8) {
|
|
182
|
+
// IPv4 address
|
|
183
|
+
uint32_t ip_addr = parse_uint32(attr_data + 4);
|
|
184
|
+
|
|
185
|
+
// Convert to string
|
|
186
|
+
struct in_addr addr;
|
|
187
|
+
addr.s_addr = htonl(ip_addr);
|
|
188
|
+
char ip_str[INET_ADDRSTRLEN];
|
|
189
|
+
if (inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN)) {
|
|
190
|
+
mapped_address.family = family;
|
|
191
|
+
mapped_address.port = port;
|
|
192
|
+
mapped_address.ip = ip_str;
|
|
193
|
+
|
|
194
|
+
// If this is XOR-MAPPED-ADDRESS, we need to XOR with magic cookie and transaction ID
|
|
195
|
+
if (attr_type == stun::ATTR_XOR_MAPPED_ADDRESS) {
|
|
196
|
+
xor_address(mapped_address, response.data() + 8); // transaction ID starts at offset 8
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
found_mapped_address = true;
|
|
200
|
+
LOG_STUN_DEBUG("Found " << (attr_type == stun::ATTR_XOR_MAPPED_ADDRESS ? "XOR-" : "")
|
|
201
|
+
<< "MAPPED-ADDRESS: " << mapped_address.ip << ":" << mapped_address.port);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
} else if (family == stun::FAMILY_IPV6 && attr_length >= 20) {
|
|
205
|
+
// IPv6 address
|
|
206
|
+
LOG_STUN_DEBUG("IPv6 address found but not fully implemented");
|
|
207
|
+
// TODO: Implement IPv6 support if needed
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Move to next attribute (attributes are padded to 4-byte boundaries)
|
|
212
|
+
size_t padded_length = (attr_length + 3) & ~3;
|
|
213
|
+
offset += padded_length;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!found_mapped_address) {
|
|
217
|
+
LOG_STUN_ERROR("No mapped address found in STUN response");
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
void StunClient::generate_transaction_id(uint8_t* transaction_id) {
|
|
225
|
+
std::random_device rd;
|
|
226
|
+
std::mt19937 gen(rd());
|
|
227
|
+
std::uniform_int_distribution<> dis(0, 255);
|
|
228
|
+
|
|
229
|
+
for (int i = 0; i < stun::TRANSACTION_ID_SIZE; ++i) {
|
|
230
|
+
transaction_id[i] = static_cast<uint8_t>(dis(gen));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
bool StunClient::send_stun_request(socket_t sock,
|
|
235
|
+
const std::string& server,
|
|
236
|
+
int port,
|
|
237
|
+
const std::vector<uint8_t>& request) {
|
|
238
|
+
LOG_STUN_DEBUG("Sending STUN request to " << server << ":" << port << " (" << request.size() << " bytes)");
|
|
239
|
+
|
|
240
|
+
// Use the socket library function to send UDP data
|
|
241
|
+
int bytes_sent = send_udp_data_to(sock, request, server, port);
|
|
242
|
+
|
|
243
|
+
if (bytes_sent < 0) {
|
|
244
|
+
LOG_STUN_ERROR("Failed to send STUN request to " << server << ":" << port);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (bytes_sent != static_cast<int>(request.size())) {
|
|
249
|
+
LOG_STUN_ERROR("Partial STUN request sent: " << bytes_sent << "/" << request.size() << " bytes");
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
LOG_STUN_DEBUG("Successfully sent STUN request to " << server << ":" << port << " (" << bytes_sent << " bytes)");
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
bool StunClient::receive_stun_response(socket_t sock,
|
|
258
|
+
std::vector<uint8_t>& response,
|
|
259
|
+
int timeout_ms) {
|
|
260
|
+
LOG_STUN_DEBUG("Waiting for STUN response with timeout " << timeout_ms << "ms");
|
|
261
|
+
|
|
262
|
+
std::string sender_ip;
|
|
263
|
+
int sender_port;
|
|
264
|
+
|
|
265
|
+
// Use the socket library function to receive UDP data with timeout
|
|
266
|
+
response = receive_udp_data_with_timeout(sock, 1024, timeout_ms, &sender_ip, &sender_port);
|
|
267
|
+
|
|
268
|
+
if (response.empty()) {
|
|
269
|
+
LOG_STUN_ERROR("Failed to receive STUN response or timeout occurred");
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
LOG_STUN_DEBUG("Received STUN response from " << sender_ip << ":" << sender_port
|
|
274
|
+
<< " (" << response.size() << " bytes)");
|
|
275
|
+
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Helper functions for parsing binary data
|
|
280
|
+
uint16_t StunClient::parse_uint16(const uint8_t* data) {
|
|
281
|
+
return (static_cast<uint16_t>(data[0]) << 8) | static_cast<uint16_t>(data[1]);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
uint32_t StunClient::parse_uint32(const uint8_t* data) {
|
|
285
|
+
return (static_cast<uint32_t>(data[0]) << 24) |
|
|
286
|
+
(static_cast<uint32_t>(data[1]) << 16) |
|
|
287
|
+
(static_cast<uint32_t>(data[2]) << 8) |
|
|
288
|
+
static_cast<uint32_t>(data[3]);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
void StunClient::write_uint16(uint8_t* data, uint16_t value) {
|
|
292
|
+
data[0] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
|
293
|
+
data[1] = static_cast<uint8_t>(value & 0xFF);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
void StunClient::write_uint32(uint8_t* data, uint32_t value) {
|
|
297
|
+
data[0] = static_cast<uint8_t>((value >> 24) & 0xFF);
|
|
298
|
+
data[1] = static_cast<uint8_t>((value >> 16) & 0xFF);
|
|
299
|
+
data[2] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
|
300
|
+
data[3] = static_cast<uint8_t>(value & 0xFF);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
void StunClient::xor_address(StunAddress& address, const uint8_t* transaction_id) {
|
|
304
|
+
// For XOR-MAPPED-ADDRESS, we need to XOR the port with the first 16 bits of magic cookie
|
|
305
|
+
// and the IP address with magic cookie
|
|
306
|
+
|
|
307
|
+
// XOR port with first 16 bits of magic cookie
|
|
308
|
+
uint16_t magic_cookie_high = (stun::MAGIC_COOKIE >> 16) & 0xFFFF;
|
|
309
|
+
address.port ^= magic_cookie_high;
|
|
310
|
+
|
|
311
|
+
// XOR IPv4 address with magic cookie
|
|
312
|
+
if (address.family == stun::FAMILY_IPV4) {
|
|
313
|
+
struct in_addr addr;
|
|
314
|
+
if (inet_pton(AF_INET, address.ip.c_str(), &addr) == 1) {
|
|
315
|
+
uint32_t ip_addr = ntohl(addr.s_addr);
|
|
316
|
+
ip_addr ^= stun::MAGIC_COOKIE;
|
|
317
|
+
addr.s_addr = htonl(ip_addr);
|
|
318
|
+
|
|
319
|
+
char ip_str[INET_ADDRSTRLEN];
|
|
320
|
+
if (inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN)) {
|
|
321
|
+
address.ip = ip_str;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// TODO: Implement IPv6 XOR if needed (requires XOR with magic cookie + transaction ID)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ICE-specific static helper function implementation
|
|
329
|
+
std::vector<uint8_t> StunClient::create_binding_request_ice(const std::string& username,
|
|
330
|
+
const std::string& password,
|
|
331
|
+
uint32_t priority,
|
|
332
|
+
bool controlling,
|
|
333
|
+
uint64_t tie_breaker,
|
|
334
|
+
bool use_candidate) {
|
|
335
|
+
// Start with basic binding request
|
|
336
|
+
std::vector<uint8_t> message = create_binding_request();
|
|
337
|
+
|
|
338
|
+
// Add ICE-specific attributes
|
|
339
|
+
size_t original_size = message.size();
|
|
340
|
+
|
|
341
|
+
// USERNAME attribute (0x0006)
|
|
342
|
+
if (!username.empty()) {
|
|
343
|
+
uint16_t attr_type = 0x0006;
|
|
344
|
+
uint16_t attr_length = username.length();
|
|
345
|
+
|
|
346
|
+
message.push_back((attr_type >> 8) & 0xFF);
|
|
347
|
+
message.push_back(attr_type & 0xFF);
|
|
348
|
+
message.push_back((attr_length >> 8) & 0xFF);
|
|
349
|
+
message.push_back(attr_length & 0xFF);
|
|
350
|
+
|
|
351
|
+
for (char c : username) {
|
|
352
|
+
message.push_back(static_cast<uint8_t>(c));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Pad to 4-byte boundary
|
|
356
|
+
while ((message.size() - original_size) % 4 != 0) {
|
|
357
|
+
message.push_back(0);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// PRIORITY attribute (0x0024)
|
|
362
|
+
{
|
|
363
|
+
uint16_t attr_type = 0x0024;
|
|
364
|
+
uint16_t attr_length = 4;
|
|
365
|
+
|
|
366
|
+
message.push_back((attr_type >> 8) & 0xFF);
|
|
367
|
+
message.push_back(attr_type & 0xFF);
|
|
368
|
+
message.push_back((attr_length >> 8) & 0xFF);
|
|
369
|
+
message.push_back(attr_length & 0xFF);
|
|
370
|
+
|
|
371
|
+
message.push_back((priority >> 24) & 0xFF);
|
|
372
|
+
message.push_back((priority >> 16) & 0xFF);
|
|
373
|
+
message.push_back((priority >> 8) & 0xFF);
|
|
374
|
+
message.push_back(priority & 0xFF);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ICE-CONTROLLING or ICE-CONTROLLED attribute
|
|
378
|
+
{
|
|
379
|
+
uint16_t attr_type = controlling ? 0x802A : 0x8029; // ICE-CONTROLLING : ICE-CONTROLLED
|
|
380
|
+
uint16_t attr_length = 8;
|
|
381
|
+
|
|
382
|
+
message.push_back((attr_type >> 8) & 0xFF);
|
|
383
|
+
message.push_back(attr_type & 0xFF);
|
|
384
|
+
message.push_back((attr_length >> 8) & 0xFF);
|
|
385
|
+
message.push_back(attr_length & 0xFF);
|
|
386
|
+
|
|
387
|
+
// Tie breaker value
|
|
388
|
+
for (int i = 7; i >= 0; i--) {
|
|
389
|
+
message.push_back((tie_breaker >> (i * 8)) & 0xFF);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// USE-CANDIDATE attribute (0x0025) - only for controlling agent
|
|
394
|
+
if (use_candidate && controlling) {
|
|
395
|
+
uint16_t attr_type = 0x0025;
|
|
396
|
+
uint16_t attr_length = 0; // No value for this attribute
|
|
397
|
+
|
|
398
|
+
message.push_back((attr_type >> 8) & 0xFF);
|
|
399
|
+
message.push_back(attr_type & 0xFF);
|
|
400
|
+
message.push_back((attr_length >> 8) & 0xFF);
|
|
401
|
+
message.push_back(attr_length & 0xFF);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Update message length
|
|
405
|
+
uint16_t total_length = message.size() - 20; // Exclude header
|
|
406
|
+
message[2] = (total_length >> 8) & 0xFF;
|
|
407
|
+
message[3] = total_length & 0xFF;
|
|
408
|
+
|
|
409
|
+
return message;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// AdvancedNatDetector implementation
|
|
413
|
+
AdvancedNatDetector::AdvancedNatDetector() {
|
|
414
|
+
stun_client_ = std::make_unique<StunClient>();
|
|
415
|
+
LOG_STUN_DEBUG("AdvancedNatDetector created");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
AdvancedNatDetector::~AdvancedNatDetector() {
|
|
419
|
+
LOG_STUN_DEBUG("AdvancedNatDetector destroyed");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
NatTypeInfo AdvancedNatDetector::detect_nat_characteristics(const std::vector<std::string>& stun_servers,
|
|
423
|
+
int timeout_ms) {
|
|
424
|
+
NatTypeInfo info;
|
|
425
|
+
|
|
426
|
+
if (stun_servers.empty()) {
|
|
427
|
+
LOG_STUN_WARN("No STUN servers provided for NAT detection");
|
|
428
|
+
info.has_nat = true; // Assume NAT if we can't test
|
|
429
|
+
info.filtering_behavior = NatBehavior::UNKNOWN;
|
|
430
|
+
info.mapping_behavior = NatBehavior::UNKNOWN;
|
|
431
|
+
info.preserves_port = false;
|
|
432
|
+
info.hairpin_support = false;
|
|
433
|
+
info.description = "No STUN servers available";
|
|
434
|
+
return info;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Basic implementation - test if we can reach STUN server
|
|
438
|
+
StunAddress public_address;
|
|
439
|
+
|
|
440
|
+
if (stun_client_->get_public_address(stun_servers[0], 3478, public_address, timeout_ms)) {
|
|
441
|
+
// Simple NAT type detection - this is a minimal implementation
|
|
442
|
+
info.has_nat = true; // Assume NAT for now
|
|
443
|
+
info.filtering_behavior = NatBehavior::ENDPOINT_INDEPENDENT;
|
|
444
|
+
info.mapping_behavior = NatBehavior::ENDPOINT_INDEPENDENT;
|
|
445
|
+
info.preserves_port = false;
|
|
446
|
+
info.hairpin_support = false;
|
|
447
|
+
info.description = "NAT detected via STUN";
|
|
448
|
+
} else {
|
|
449
|
+
info.has_nat = false;
|
|
450
|
+
info.filtering_behavior = NatBehavior::ENDPOINT_INDEPENDENT;
|
|
451
|
+
info.mapping_behavior = NatBehavior::ENDPOINT_INDEPENDENT;
|
|
452
|
+
info.preserves_port = true;
|
|
453
|
+
info.hairpin_support = true;
|
|
454
|
+
info.description = "Open internet connection or STUN failure";
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return info;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
bool AdvancedNatDetector::test_hairpin_support(const std::string& stun_server, int timeout_ms) {
|
|
461
|
+
// Simplified implementation - return false for now
|
|
462
|
+
LOG_STUN_DEBUG("Testing hairpin support (not fully implemented)");
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
bool AdvancedNatDetector::test_port_preservation(const std::vector<std::string>& stun_servers, int timeout_ms) {
|
|
467
|
+
// Simplified implementation - return false for now
|
|
468
|
+
LOG_STUN_DEBUG("Testing port preservation (not fully implemented)");
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
NatBehavior AdvancedNatDetector::test_filtering_behavior(const std::vector<std::string>& stun_servers, int timeout_ms) {
|
|
473
|
+
// Simplified implementation - return endpoint independent for now
|
|
474
|
+
LOG_STUN_DEBUG("Testing filtering behavior (not fully implemented)");
|
|
475
|
+
return NatBehavior::ENDPOINT_INDEPENDENT;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
NatBehavior AdvancedNatDetector::test_mapping_behavior(const std::vector<std::string>& stun_servers, int timeout_ms) {
|
|
479
|
+
// Simplified implementation - return endpoint independent for now
|
|
480
|
+
LOG_STUN_DEBUG("Testing mapping behavior (not fully implemented)");
|
|
481
|
+
return NatBehavior::ENDPOINT_INDEPENDENT;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
} // namespace librats
|