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,1376 @@
1
+ #include "socket.h"
2
+ #include "network_utils.h"
3
+ #include "logger.h"
4
+ #include <iostream>
5
+ #include <cstring>
6
+ #include <mutex>
7
+ #include <thread>
8
+ #include <chrono>
9
+ #ifndef _WIN32
10
+ #include <fcntl.h> // for O_NONBLOCK
11
+ #include <errno.h> // for errno
12
+ #endif
13
+
14
+ // Socket module logging macros
15
+ #define LOG_SOCKET_DEBUG(message) LOG_DEBUG("socket", message)
16
+ #define LOG_SOCKET_INFO(message) LOG_INFO("socket", message)
17
+ #define LOG_SOCKET_WARN(message) LOG_WARN("socket", message)
18
+ #define LOG_SOCKET_ERROR(message) LOG_ERROR("socket", message)
19
+
20
+ namespace librats {
21
+
22
+ // Static flag to track socket library initialization
23
+ static bool socket_library_initialized = false;
24
+ static std::mutex socket_init_mutex;
25
+
26
+ // Socket Library Initialization
27
+ bool init_socket_library() {
28
+ std::lock_guard<std::mutex> lock(socket_init_mutex);
29
+
30
+ if (socket_library_initialized) {
31
+ return true; // Already initialized
32
+ }
33
+
34
+ #ifdef _WIN32
35
+ WSADATA wsaData;
36
+ int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
37
+ if (result != 0) {
38
+ LOG_SOCKET_ERROR("WSAStartup failed: " << result);
39
+ return false;
40
+ }
41
+ LOG_SOCKET_INFO("Windows Socket API initialized");
42
+ #endif
43
+
44
+ socket_library_initialized = true;
45
+ LOG_SOCKET_INFO("Socket library initialized");
46
+ return true;
47
+ }
48
+
49
+ void cleanup_socket_library() {
50
+ std::lock_guard<std::mutex> lock(socket_init_mutex);
51
+
52
+ if (!socket_library_initialized) {
53
+ return; // Not initialized or already cleaned up
54
+ }
55
+
56
+ #ifdef _WIN32
57
+ WSACleanup();
58
+ LOG_SOCKET_INFO("Windows Socket API cleaned up");
59
+ #endif
60
+
61
+ socket_library_initialized = false;
62
+ LOG_SOCKET_INFO("Socket library cleaned up");
63
+ }
64
+
65
+ // Helper function for connection with timeout
66
+ bool connect_with_timeout(socket_t socket, struct sockaddr* addr, socklen_t addr_len, int timeout_ms) {
67
+ // Set socket to non-blocking mode
68
+ if (!set_socket_nonblocking(socket)) {
69
+ LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode for timeout connection");
70
+ return false;
71
+ }
72
+
73
+ // Attempt to connect
74
+ int result = connect(socket, addr, addr_len);
75
+
76
+ if (result == 0) {
77
+ // Connection succeeded immediately - this is rare but possible
78
+ LOG_SOCKET_DEBUG("Connection succeeded immediately");
79
+ return true;
80
+ }
81
+
82
+ // Check for expected non-blocking connect error
83
+ #ifdef _WIN32
84
+ int error = WSAGetLastError();
85
+ if (error != WSAEWOULDBLOCK) {
86
+ LOG_SOCKET_ERROR("Connect failed immediately with error: " << error);
87
+ return false;
88
+ }
89
+ #else
90
+ int error = errno;
91
+ if (error != EINPROGRESS) {
92
+ LOG_SOCKET_ERROR("Connect failed immediately with error: " << strerror(error));
93
+ return false;
94
+ }
95
+ #endif
96
+
97
+ // Use select to wait for connection with timeout
98
+ fd_set write_fds, error_fds;
99
+ FD_ZERO(&write_fds);
100
+ FD_ZERO(&error_fds);
101
+ FD_SET(socket, &write_fds);
102
+ FD_SET(socket, &error_fds);
103
+
104
+ struct timeval timeout;
105
+ timeout.tv_sec = timeout_ms / 1000;
106
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
107
+
108
+ LOG_SOCKET_DEBUG("Waiting for connection with timeout " << timeout_ms << "ms");
109
+ int select_result = select(socket + 1, nullptr, &write_fds, &error_fds, &timeout);
110
+
111
+ if (select_result == 0) {
112
+ // Timeout occurred
113
+ LOG_SOCKET_WARN("Connection timeout after " << timeout_ms << "ms");
114
+ return false;
115
+ } else if (select_result < 0) {
116
+ // Select error
117
+ #ifdef _WIN32
118
+ LOG_SOCKET_ERROR("Select error during connect: " << WSAGetLastError());
119
+ #else
120
+ LOG_SOCKET_ERROR("Select error during connect: " << strerror(errno));
121
+ #endif
122
+ return false;
123
+ }
124
+
125
+ // Check if connection completed successfully or failed
126
+ if (FD_ISSET(socket, &error_fds)) {
127
+ // Connection failed
128
+ LOG_SOCKET_ERROR("Connection failed (error detected)");
129
+ return false;
130
+ }
131
+
132
+ if (FD_ISSET(socket, &write_fds)) {
133
+ // Check for actual connection success using getsockopt
134
+ int sock_error;
135
+ socklen_t len = sizeof(sock_error);
136
+ if (getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&sock_error, &len) < 0) {
137
+ LOG_SOCKET_ERROR("Failed to get socket error status");
138
+ return false;
139
+ }
140
+
141
+ if (sock_error != 0) {
142
+ #ifdef _WIN32
143
+ LOG_SOCKET_ERROR("Connection failed with error: " << sock_error);
144
+ #else
145
+ LOG_SOCKET_ERROR("Connection failed with error: " << strerror(sock_error));
146
+ #endif
147
+ return false;
148
+ }
149
+
150
+ // Connection succeeded
151
+ LOG_SOCKET_DEBUG("Connection succeeded within timeout");
152
+ return true;
153
+ }
154
+
155
+ // This shouldn't happen
156
+ LOG_SOCKET_ERROR("Unexpected select result state");
157
+ return false;
158
+ }
159
+
160
+ // TCP Socket Functions
161
+ socket_t create_tcp_client(const std::string& host, int port, int timeout_ms) {
162
+ if (timeout_ms > 0) {
163
+ LOG_SOCKET_DEBUG("Creating TCP client socket (dual stack) for " << host << ":" << port << " with timeout " << timeout_ms << "ms");
164
+ } else {
165
+ LOG_SOCKET_DEBUG("Creating TCP client socket (dual stack) for " << host << ":" << port);
166
+ }
167
+
168
+ // Try IPv6 first
169
+ socket_t client_socket = create_tcp_client_v6(host, port, timeout_ms);
170
+ if (client_socket != INVALID_SOCKET_VALUE) {
171
+ LOG_SOCKET_INFO("Successfully connected using IPv6");
172
+ return client_socket;
173
+ }
174
+
175
+ // Fall back to IPv4
176
+ LOG_SOCKET_DEBUG("IPv6 connection failed, trying IPv4");
177
+ client_socket = create_tcp_client_v4(host, port, timeout_ms);
178
+ if (client_socket != INVALID_SOCKET_VALUE) {
179
+ LOG_SOCKET_INFO("Successfully connected using IPv4");
180
+ return client_socket;
181
+ }
182
+
183
+ LOG_SOCKET_ERROR("Failed to connect using both IPv6 and IPv4");
184
+ return INVALID_SOCKET_VALUE;
185
+ }
186
+
187
+ socket_t create_tcp_client_v4(const std::string& host, int port, int timeout_ms) {
188
+ if (timeout_ms > 0) {
189
+ LOG_SOCKET_DEBUG("Creating TCP client socket for " << host << ":" << port << " with timeout " << timeout_ms << "ms");
190
+ } else {
191
+ LOG_SOCKET_DEBUG("Creating TCP client socket for " << host << ":" << port);
192
+ }
193
+
194
+ // Validate port number
195
+ if (port < 0 || port > 65535) {
196
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
197
+ return INVALID_SOCKET_VALUE;
198
+ }
199
+
200
+ socket_t client_socket = socket(AF_INET, SOCK_STREAM, 0);
201
+ if (client_socket == INVALID_SOCKET_VALUE) {
202
+ LOG_SOCKET_ERROR("Failed to create client socket");
203
+ return INVALID_SOCKET_VALUE;
204
+ }
205
+
206
+ sockaddr_in server_addr;
207
+ memset(&server_addr, 0, sizeof(server_addr));
208
+ server_addr.sin_family = AF_INET;
209
+ server_addr.sin_port = htons(port);
210
+
211
+ // Resolve hostname to IP address
212
+ std::string resolved_ip = network_utils::resolve_hostname(host);
213
+ if (resolved_ip.empty()) {
214
+ LOG_SOCKET_ERROR("Failed to resolve hostname: " << host);
215
+ close_socket(client_socket);
216
+ return INVALID_SOCKET_VALUE;
217
+ }
218
+
219
+ // Convert IP address from string to binary form
220
+ if (inet_pton(AF_INET, resolved_ip.c_str(), &server_addr.sin_addr) <= 0) {
221
+ LOG_SOCKET_ERROR("Invalid address: " << resolved_ip);
222
+ close_socket(client_socket);
223
+ return INVALID_SOCKET_VALUE;
224
+ }
225
+
226
+ // Connect to server
227
+ LOG_SOCKET_DEBUG("Connecting to " << resolved_ip << ":" << port);
228
+ bool connection_success;
229
+
230
+ if (timeout_ms > 0) {
231
+ // Use timeout connection
232
+ connection_success = connect_with_timeout(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr), timeout_ms);
233
+ } else {
234
+ // Use blocking connection
235
+ connection_success = (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) != SOCKET_ERROR_VALUE);
236
+ }
237
+
238
+ if (!connection_success) {
239
+ if (timeout_ms > 0) {
240
+ LOG_SOCKET_ERROR("Connection to " << resolved_ip << ":" << port << " failed or timed out after " << timeout_ms << "ms");
241
+ } else {
242
+ LOG_SOCKET_ERROR("Connection to " << resolved_ip << ":" << port << " failed");
243
+ }
244
+ close_socket(client_socket);
245
+ return INVALID_SOCKET_VALUE;
246
+ }
247
+
248
+ LOG_SOCKET_INFO("Successfully connected to " << resolved_ip << ":" << port);
249
+ return client_socket;
250
+ }
251
+
252
+ socket_t create_tcp_client_v6(const std::string& host, int port, int timeout_ms) {
253
+ if (timeout_ms > 0) {
254
+ LOG_SOCKET_DEBUG("Creating TCP client socket for IPv6 " << host << ":" << port << " with timeout " << timeout_ms << "ms");
255
+ } else {
256
+ LOG_SOCKET_DEBUG("Creating TCP client socket for IPv6 " << host << ":" << port);
257
+ }
258
+
259
+ // Validate port number
260
+ if (port < 0 || port > 65535) {
261
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
262
+ return INVALID_SOCKET_VALUE;
263
+ }
264
+
265
+ socket_t client_socket = socket(AF_INET6, SOCK_STREAM, 0);
266
+ if (client_socket == INVALID_SOCKET_VALUE) {
267
+ LOG_SOCKET_ERROR("Failed to create IPv6 client socket");
268
+ return INVALID_SOCKET_VALUE;
269
+ }
270
+
271
+ sockaddr_in6 server_addr;
272
+ memset(&server_addr, 0, sizeof(server_addr));
273
+ server_addr.sin6_family = AF_INET6;
274
+ server_addr.sin6_port = htons(port);
275
+
276
+ // Resolve hostname to IPv6 address
277
+ std::string resolved_ip = network_utils::resolve_hostname_v6(host);
278
+ if (resolved_ip.empty()) {
279
+ LOG_SOCKET_ERROR("Failed to resolve hostname to IPv6: " << host);
280
+ close_socket(client_socket);
281
+ return INVALID_SOCKET_VALUE;
282
+ }
283
+
284
+ // Convert IPv6 address from string to binary form
285
+ if (inet_pton(AF_INET6, resolved_ip.c_str(), &server_addr.sin6_addr) <= 0) {
286
+ LOG_SOCKET_ERROR("Invalid IPv6 address: " << resolved_ip);
287
+ close_socket(client_socket);
288
+ return INVALID_SOCKET_VALUE;
289
+ }
290
+
291
+ // Connect to server
292
+ LOG_SOCKET_DEBUG("Connecting to IPv6 " << resolved_ip << ":" << port);
293
+ bool connection_success;
294
+
295
+ if (timeout_ms > 0) {
296
+ // Use timeout connection
297
+ connection_success = connect_with_timeout(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr), timeout_ms);
298
+ } else {
299
+ // Use blocking connection
300
+ connection_success = (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) != SOCKET_ERROR_VALUE);
301
+ }
302
+
303
+ if (!connection_success) {
304
+ if (timeout_ms > 0) {
305
+ LOG_SOCKET_ERROR("Connection to IPv6 " << resolved_ip << ":" << port << " failed or timed out after " << timeout_ms << "ms");
306
+ } else {
307
+ LOG_SOCKET_ERROR("Connection to IPv6 " << resolved_ip << ":" << port << " failed");
308
+ }
309
+ close_socket(client_socket);
310
+ return INVALID_SOCKET_VALUE;
311
+ }
312
+
313
+ LOG_SOCKET_INFO("Successfully connected to IPv6 " << resolved_ip << ":" << port);
314
+ return client_socket;
315
+ }
316
+
317
+ socket_t create_tcp_server(int port, int backlog, const std::string& bind_address) {
318
+ LOG_SOCKET_DEBUG("Creating TCP server socket (dual stack) on port " << port <<
319
+ (bind_address.empty() ? "" : " bound to " + bind_address));
320
+
321
+ // Validate port number
322
+ if (port < 0 || port > 65535) {
323
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
324
+ return INVALID_SOCKET_VALUE;
325
+ }
326
+
327
+ socket_t server_socket = socket(AF_INET6, SOCK_STREAM, 0);
328
+ if (server_socket == INVALID_SOCKET_VALUE) {
329
+ LOG_SOCKET_ERROR("Failed to create dual stack server socket");
330
+ return INVALID_SOCKET_VALUE;
331
+ }
332
+
333
+ // Set socket option to reuse address
334
+ int opt = 1;
335
+ if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
336
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
337
+ LOG_SOCKET_ERROR("Failed to set dual stack socket options");
338
+ close_socket(server_socket);
339
+ return INVALID_SOCKET_VALUE;
340
+ }
341
+
342
+ // Disable IPv6-only mode to allow IPv4 connections
343
+ int ipv6_only = 0;
344
+ if (setsockopt(server_socket, IPPROTO_IPV6, IPV6_V6ONLY,
345
+ (char*)&ipv6_only, sizeof(ipv6_only)) == SOCKET_ERROR_VALUE) {
346
+ LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only");
347
+ }
348
+
349
+ sockaddr_in6 server_addr;
350
+ memset(&server_addr, 0, sizeof(server_addr));
351
+ server_addr.sin6_family = AF_INET6;
352
+ server_addr.sin6_port = htons(port);
353
+
354
+ // Set bind address (default to all interfaces if empty)
355
+ if (bind_address.empty()) {
356
+ server_addr.sin6_addr = in6addr_any;
357
+ } else {
358
+ if (inet_pton(AF_INET6, bind_address.c_str(), &server_addr.sin6_addr) != 1) {
359
+ LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
360
+ close_socket(server_socket);
361
+ return INVALID_SOCKET_VALUE;
362
+ }
363
+ }
364
+
365
+ // Bind socket to address
366
+ LOG_SOCKET_DEBUG("Binding dual stack server socket to port " << port);
367
+ if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR_VALUE) {
368
+ LOG_SOCKET_ERROR("Failed to bind dual stack server socket to port " << port);
369
+ close_socket(server_socket);
370
+ return INVALID_SOCKET_VALUE;
371
+ }
372
+
373
+ // Listen for connections
374
+ if (listen(server_socket, backlog) == SOCKET_ERROR_VALUE) {
375
+ LOG_SOCKET_ERROR("Failed to listen on dual stack server socket");
376
+ close_socket(server_socket);
377
+ return INVALID_SOCKET_VALUE;
378
+ }
379
+
380
+ LOG_SOCKET_INFO("Dual stack server listening on port " << port << " (backlog: " << backlog << ")");
381
+ return server_socket;
382
+ }
383
+
384
+ socket_t create_tcp_server_v4(int port, int backlog, const std::string& bind_address) {
385
+ LOG_SOCKET_DEBUG("Creating TCP server socket on port " << port <<
386
+ (bind_address.empty() ? "" : " bound to " + bind_address));
387
+
388
+ // Validate port number
389
+ if (port < 0 || port > 65535) {
390
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
391
+ return INVALID_SOCKET_VALUE;
392
+ }
393
+
394
+ socket_t server_socket = socket(AF_INET, SOCK_STREAM, 0);
395
+ if (server_socket == INVALID_SOCKET_VALUE) {
396
+ LOG_SOCKET_ERROR("Failed to create server socket");
397
+ return INVALID_SOCKET_VALUE;
398
+ }
399
+
400
+ // Set socket option to reuse address
401
+ int opt = 1;
402
+ if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
403
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
404
+ LOG_SOCKET_ERROR("Failed to set socket options");
405
+ close_socket(server_socket);
406
+ return INVALID_SOCKET_VALUE;
407
+ }
408
+
409
+ sockaddr_in server_addr;
410
+ memset(&server_addr, 0, sizeof(server_addr));
411
+ server_addr.sin_family = AF_INET;
412
+ server_addr.sin_port = htons(port);
413
+
414
+ // Set bind address (default to all interfaces if empty)
415
+ if (bind_address.empty()) {
416
+ server_addr.sin_addr.s_addr = INADDR_ANY;
417
+ } else {
418
+ if (inet_pton(AF_INET, bind_address.c_str(), &server_addr.sin_addr) != 1) {
419
+ LOG_SOCKET_ERROR("Invalid IPv4 bind address: " << bind_address);
420
+ close_socket(server_socket);
421
+ return INVALID_SOCKET_VALUE;
422
+ }
423
+ }
424
+
425
+ // Bind socket to address
426
+ LOG_SOCKET_DEBUG("Binding server socket to port " << port);
427
+ if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR_VALUE) {
428
+ LOG_SOCKET_ERROR("Failed to bind server socket to port " << port);
429
+ close_socket(server_socket);
430
+ return INVALID_SOCKET_VALUE;
431
+ }
432
+
433
+ // Listen for connections
434
+ if (listen(server_socket, backlog) == SOCKET_ERROR_VALUE) {
435
+ LOG_SOCKET_ERROR("Failed to listen on server socket");
436
+ close_socket(server_socket);
437
+ return INVALID_SOCKET_VALUE;
438
+ }
439
+
440
+ LOG_SOCKET_INFO("Server listening on port " << port << " (backlog: " << backlog << ")");
441
+ return server_socket;
442
+ }
443
+
444
+ socket_t create_tcp_server_v6(int port, int backlog, const std::string& bind_address) {
445
+ LOG_SOCKET_DEBUG("Creating TCP server socket on IPv6 port " << port <<
446
+ (bind_address.empty() ? "" : " bound to " + bind_address));
447
+
448
+ // Validate port number
449
+ if (port < 0 || port > 65535) {
450
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
451
+ return INVALID_SOCKET_VALUE;
452
+ }
453
+
454
+ socket_t server_socket = socket(AF_INET6, SOCK_STREAM, 0);
455
+ if (server_socket == INVALID_SOCKET_VALUE) {
456
+ LOG_SOCKET_ERROR("Failed to create IPv6 server socket");
457
+ return INVALID_SOCKET_VALUE;
458
+ }
459
+
460
+ // Set socket option to reuse address
461
+ int opt = 1;
462
+ if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
463
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
464
+ LOG_SOCKET_ERROR("Failed to set IPv6 socket options");
465
+ close_socket(server_socket);
466
+ return INVALID_SOCKET_VALUE;
467
+ }
468
+
469
+ sockaddr_in6 server_addr;
470
+ memset(&server_addr, 0, sizeof(server_addr));
471
+ server_addr.sin6_family = AF_INET6;
472
+ server_addr.sin6_port = htons(port);
473
+
474
+ // Set bind address (default to all interfaces if empty)
475
+ if (bind_address.empty()) {
476
+ server_addr.sin6_addr = in6addr_any;
477
+ } else {
478
+ if (inet_pton(AF_INET6, bind_address.c_str(), &server_addr.sin6_addr) != 1) {
479
+ LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
480
+ close_socket(server_socket);
481
+ return INVALID_SOCKET_VALUE;
482
+ }
483
+ }
484
+
485
+ // Bind socket to address
486
+ LOG_SOCKET_DEBUG("Binding IPv6 server socket to port " << port);
487
+ if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR_VALUE) {
488
+ LOG_SOCKET_ERROR("Failed to bind IPv6 server socket to port " << port);
489
+ close_socket(server_socket);
490
+ return INVALID_SOCKET_VALUE;
491
+ }
492
+
493
+ // Listen for connections
494
+ if (listen(server_socket, backlog) == SOCKET_ERROR_VALUE) {
495
+ LOG_SOCKET_ERROR("Failed to listen on IPv6 server socket");
496
+ close_socket(server_socket);
497
+ return INVALID_SOCKET_VALUE;
498
+ }
499
+
500
+ LOG_SOCKET_INFO("IPv6 server listening on port " << port << " (backlog: " << backlog << ")");
501
+ return server_socket;
502
+ }
503
+
504
+ socket_t accept_client(socket_t server_socket) {
505
+ sockaddr_storage client_addr;
506
+ socklen_t client_addr_len = sizeof(client_addr);
507
+
508
+ socket_t client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
509
+ if (client_socket == INVALID_SOCKET_VALUE) {
510
+ LOG_SOCKET_ERROR("Failed to accept client connection");
511
+ return INVALID_SOCKET_VALUE;
512
+ }
513
+
514
+ // Log client information based on address family
515
+ if (client_addr.ss_family == AF_INET) {
516
+ char client_ip[INET_ADDRSTRLEN];
517
+ struct sockaddr_in* addr_in = (struct sockaddr_in*)&client_addr;
518
+ inet_ntop(AF_INET, &addr_in->sin_addr, client_ip, INET_ADDRSTRLEN);
519
+ LOG_SOCKET_INFO("Client connected from " << client_ip << ":" << ntohs(addr_in->sin_port));
520
+ } else if (client_addr.ss_family == AF_INET6) {
521
+ char client_ip[INET6_ADDRSTRLEN];
522
+ struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&client_addr;
523
+ inet_ntop(AF_INET6, &addr_in6->sin6_addr, client_ip, INET6_ADDRSTRLEN);
524
+ LOG_SOCKET_INFO("Client connected from IPv6 [" << client_ip << "]:" << ntohs(addr_in6->sin6_port));
525
+ } else {
526
+ LOG_SOCKET_INFO("Client connected from unknown address family");
527
+ }
528
+
529
+ return client_socket;
530
+ }
531
+
532
+ std::string get_peer_address(socket_t socket) {
533
+ sockaddr_storage peer_addr;
534
+ socklen_t peer_addr_len = sizeof(peer_addr);
535
+
536
+ if (getpeername(socket, (struct sockaddr*)&peer_addr, &peer_addr_len) == SOCKET_ERROR_VALUE) {
537
+ LOG_SOCKET_ERROR("Failed to get peer address for socket " << socket);
538
+ return "";
539
+ }
540
+
541
+ std::string peer_ip;
542
+ uint16_t peer_port = 0;
543
+
544
+ if (peer_addr.ss_family == AF_INET) {
545
+ char ip_str[INET_ADDRSTRLEN];
546
+ struct sockaddr_in* addr_in = (struct sockaddr_in*)&peer_addr;
547
+ inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
548
+ peer_ip = ip_str;
549
+ peer_port = ntohs(addr_in->sin_port);
550
+ } else if (peer_addr.ss_family == AF_INET6) {
551
+ char ip_str[INET6_ADDRSTRLEN];
552
+ struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&peer_addr;
553
+ inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
554
+ peer_ip = ip_str;
555
+ peer_port = ntohs(addr_in6->sin6_port);
556
+ } else {
557
+ LOG_SOCKET_ERROR("Unknown address family for socket " << socket);
558
+ return "";
559
+ }
560
+
561
+ return peer_ip + ":" + std::to_string(peer_port);
562
+ }
563
+
564
+ int send_tcp_data(socket_t socket, const std::vector<uint8_t>& data) {
565
+ LOG_SOCKET_DEBUG("Sending " << data.size() << " bytes to TCP socket " << socket);
566
+
567
+ size_t total_sent = 0;
568
+ const char* buffer = reinterpret_cast<const char*>(data.data());
569
+ size_t remaining = data.size();
570
+
571
+ while (remaining > 0) {
572
+ #ifdef _WIN32
573
+ int bytes_sent = send(socket, buffer + total_sent, remaining, 0);
574
+ #else
575
+ // Use MSG_NOSIGNAL to prevent SIGPIPE on broken connections
576
+ int bytes_sent = send(socket, buffer + total_sent, remaining, MSG_NOSIGNAL);
577
+ #endif
578
+ if (bytes_sent == SOCKET_ERROR_VALUE) {
579
+ #ifdef _WIN32
580
+ int error = WSAGetLastError();
581
+ if (error == WSAEWOULDBLOCK) {
582
+ // Non-blocking socket would block, try again
583
+ continue;
584
+ }
585
+ #else
586
+ int error = errno;
587
+ if (error == EAGAIN || error == EWOULDBLOCK) {
588
+ // Non-blocking socket would block, try again
589
+ continue;
590
+ }
591
+ if (error == EPIPE || error == ECONNRESET || error == ENOTCONN) {
592
+ // Connection closed by peer - this is expected during shutdown
593
+ LOG_SOCKET_DEBUG("Connection closed during send to socket " << socket << " (error: " << strerror(error) << ")");
594
+ return -1;
595
+ }
596
+ #endif
597
+ LOG_SOCKET_ERROR("Failed to send TCP data to socket " << socket << " (error: " << error << ")");
598
+ return -1;
599
+ }
600
+
601
+ if (bytes_sent == 0) {
602
+ LOG_SOCKET_ERROR("Connection closed by peer during send on socket " << socket);
603
+ return -1;
604
+ }
605
+
606
+ total_sent += bytes_sent;
607
+ remaining -= bytes_sent;
608
+ LOG_SOCKET_DEBUG("Sent " << bytes_sent << " bytes, " << remaining << " remaining");
609
+ }
610
+
611
+ LOG_SOCKET_DEBUG("Successfully sent all " << total_sent << " bytes to TCP socket " << socket);
612
+ return static_cast<int>(total_sent);
613
+ }
614
+
615
+ std::vector<uint8_t> receive_tcp_data(socket_t socket, size_t buffer_size) {
616
+ if (buffer_size == 0) {
617
+ buffer_size = 1024; // Default buffer size
618
+ }
619
+
620
+ std::vector<uint8_t> buffer(buffer_size);
621
+
622
+ int bytes_received = recv(socket, reinterpret_cast<char*>(buffer.data()), buffer_size, 0);
623
+ if (bytes_received == SOCKET_ERROR_VALUE) {
624
+ #ifdef _WIN32
625
+ int error = WSAGetLastError();
626
+ if (error == WSAEWOULDBLOCK) {
627
+ // No data available on non-blocking socket - this is normal
628
+ return std::vector<uint8_t>();
629
+ }
630
+ LOG_SOCKET_ERROR("Failed to receive TCP data from socket " << socket << " (error: " << error << ")");
631
+ #else
632
+ int error = errno;
633
+ if (error == EAGAIN || error == EWOULDBLOCK) {
634
+ // No data available on non-blocking socket - this is normal
635
+ return std::vector<uint8_t>();
636
+ }
637
+ LOG_SOCKET_ERROR("Failed to receive TCP data from socket " << socket << " (error: " << strerror(error) << ")");
638
+ #endif
639
+ return std::vector<uint8_t>();
640
+ }
641
+
642
+ if (bytes_received == 0) {
643
+ LOG_SOCKET_INFO("Connection closed by peer on socket " << socket);
644
+ return std::vector<uint8_t>();
645
+ }
646
+
647
+ LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from TCP socket " << socket);
648
+
649
+ // Resize to actual received size
650
+ buffer.resize(bytes_received);
651
+ return buffer;
652
+ }
653
+
654
+ // Large message handling with length-prefixed framing
655
+ int send_tcp_message_framed(socket_t socket, const std::vector<uint8_t>& message) {
656
+ // Create length prefix (4 bytes, network byte order)
657
+ uint32_t message_length = static_cast<uint32_t>(message.size());
658
+ uint32_t length_prefix = htonl(message_length);
659
+
660
+ // Send length prefix first
661
+ std::vector<uint8_t> prefix_data(reinterpret_cast<const uint8_t*>(&length_prefix),
662
+ reinterpret_cast<const uint8_t*>(&length_prefix) + 4);
663
+ int prefix_sent = send_tcp_data(socket, prefix_data);
664
+ if (prefix_sent != 4) {
665
+ LOG_SOCKET_ERROR("Failed to send message length prefix to socket " << socket);
666
+ return -1;
667
+ }
668
+
669
+ // Send the actual message
670
+ int message_sent = send_tcp_data(socket, message);
671
+ if (message_sent != static_cast<int>(message.size())) {
672
+ LOG_SOCKET_ERROR("Failed to send complete message to socket " << socket);
673
+ return -1;
674
+ }
675
+
676
+ LOG_SOCKET_DEBUG("Successfully sent framed message (" << message.size() << " bytes) to socket " << socket);
677
+ return prefix_sent + message_sent;
678
+ }
679
+
680
+ std::vector<uint8_t> receive_exact_bytes(socket_t socket, size_t num_bytes) {
681
+ std::vector<uint8_t> result;
682
+ result.reserve(num_bytes);
683
+
684
+ size_t total_received = 0;
685
+ while (total_received < num_bytes) {
686
+ std::vector<uint8_t> buffer(num_bytes - total_received);
687
+ int bytes_received = recv(socket, reinterpret_cast<char*>(buffer.data()), buffer.size(), 0);
688
+
689
+ if (bytes_received == SOCKET_ERROR_VALUE) {
690
+ #ifdef _WIN32
691
+ int error = WSAGetLastError();
692
+ if (error == WSAEWOULDBLOCK) {
693
+ // No data available on non-blocking socket - try again
694
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
695
+ continue;
696
+ }
697
+ LOG_SOCKET_ERROR("Failed to receive exact bytes from socket " << socket << " (error: " << error << ")");
698
+ #else
699
+ int error = errno;
700
+ if (error == EAGAIN || error == EWOULDBLOCK) {
701
+ // No data available on non-blocking socket - try again
702
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
703
+ continue;
704
+ }
705
+ LOG_SOCKET_ERROR("Failed to receive exact bytes from socket " << socket << " (error: " << strerror(error) << ")");
706
+ #endif
707
+ return std::vector<uint8_t>();
708
+ }
709
+
710
+ if (bytes_received == 0) {
711
+ LOG_SOCKET_INFO("Connection closed by peer while receiving exact bytes on socket " << socket);
712
+ return std::vector<uint8_t>();
713
+ }
714
+
715
+ result.insert(result.end(), buffer.begin(), buffer.begin() + bytes_received);
716
+ total_received += bytes_received;
717
+ }
718
+
719
+ LOG_SOCKET_DEBUG("Successfully received " << total_received << " exact bytes from socket " << socket);
720
+ return result;
721
+ }
722
+
723
+ std::vector<uint8_t> receive_tcp_message_framed(socket_t socket) {
724
+ // First, receive the 4-byte length prefix
725
+ std::vector<uint8_t> length_data = receive_exact_bytes(socket, 4);
726
+ if (length_data.size() != 4) {
727
+ if (!length_data.empty()) {
728
+ LOG_SOCKET_ERROR("Failed to receive complete length prefix from socket " << socket << " (got " << length_data.size() << " bytes)");
729
+ }
730
+ return std::vector<uint8_t>();
731
+ }
732
+
733
+ // Extract message length (convert from network byte order)
734
+ uint32_t length_prefix;
735
+ memcpy(&length_prefix, length_data.data(), 4);
736
+ uint32_t message_length = ntohl(length_prefix);
737
+
738
+ // Validate message length (prevent excessive memory allocation)
739
+ if (message_length == 0) {
740
+ LOG_SOCKET_DEBUG("Received keep-alive message (length 0) from socket " << socket);
741
+ return std::vector<uint8_t>();
742
+ }
743
+
744
+ if (message_length > 100 * 1024 * 1024) { // 100MB limit
745
+ LOG_SOCKET_ERROR("Message length too large: " << message_length << " bytes from socket " << socket);
746
+ return std::vector<uint8_t>();
747
+ }
748
+
749
+ // Receive the actual message
750
+ std::vector<uint8_t> message = receive_exact_bytes(socket, message_length);
751
+ if (message.size() != message_length) {
752
+ LOG_SOCKET_ERROR("Failed to receive complete message from socket " << socket << " (expected " << message_length << " bytes, got " << message.size() << ")");
753
+ return std::vector<uint8_t>();
754
+ }
755
+
756
+ LOG_SOCKET_DEBUG("Successfully received framed message (" << message_length << " bytes) from socket " << socket);
757
+ return message;
758
+ }
759
+
760
+ // Convenience functions for string compatibility
761
+ int send_tcp_string(socket_t socket, const std::string& data) {
762
+ std::vector<uint8_t> binary_data(data.begin(), data.end());
763
+ return send_tcp_data(socket, binary_data);
764
+ }
765
+
766
+ std::string receive_tcp_string(socket_t socket, size_t buffer_size) {
767
+ std::vector<uint8_t> binary_data = receive_tcp_data(socket, buffer_size);
768
+ if (binary_data.empty()) {
769
+ return "";
770
+ }
771
+ return std::string(binary_data.begin(), binary_data.end());
772
+ }
773
+
774
+ int send_tcp_string_framed(socket_t socket, const std::string& message) {
775
+ std::vector<uint8_t> binary_message(message.begin(), message.end());
776
+ return send_tcp_message_framed(socket, binary_message);
777
+ }
778
+
779
+ std::string receive_tcp_string_framed(socket_t socket) {
780
+ std::vector<uint8_t> binary_message = receive_tcp_message_framed(socket);
781
+ if (binary_message.empty()) {
782
+ return "";
783
+ }
784
+ return std::string(binary_message.begin(), binary_message.end());
785
+ }
786
+
787
+ // UDP Socket Functions
788
+ socket_t create_udp_socket(int port, const std::string& bind_address) {
789
+ LOG_SOCKET_DEBUG("Creating dual stack UDP socket on port " << port <<
790
+ (bind_address.empty() ? "" : " bound to " + bind_address));
791
+
792
+ // Validate port number
793
+ if (port < 0 || port > 65535) {
794
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
795
+ return INVALID_SOCKET_VALUE;
796
+ }
797
+
798
+ socket_t udp_socket = socket(AF_INET6, SOCK_DGRAM, 0);
799
+ if (udp_socket == INVALID_SOCKET_VALUE) {
800
+ #ifdef _WIN32
801
+ LOG_SOCKET_ERROR("Failed to create dual stack UDP socket (error: " << WSAGetLastError() << ")");
802
+ #else
803
+ LOG_SOCKET_ERROR("Failed to create dual stack UDP socket (error: " << strerror(errno) << ")");
804
+ #endif
805
+ return INVALID_SOCKET_VALUE;
806
+ }
807
+
808
+ // Set socket option to reuse address
809
+ int opt = 1;
810
+ if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR,
811
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
812
+ LOG_SOCKET_ERROR("Failed to set dual stack UDP socket options");
813
+ close_socket(udp_socket);
814
+ return INVALID_SOCKET_VALUE;
815
+ }
816
+
817
+ // Disable IPv6-only mode to allow IPv4 connections
818
+ int ipv6_only = 0;
819
+ if (setsockopt(udp_socket, IPPROTO_IPV6, IPV6_V6ONLY,
820
+ (char*)&ipv6_only, sizeof(ipv6_only)) == SOCKET_ERROR_VALUE) {
821
+ LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only");
822
+ }
823
+
824
+ // Always bind the socket - this is required for receiving data
825
+ sockaddr_in6 addr;
826
+ memset(&addr, 0, sizeof(addr));
827
+ addr.sin6_family = AF_INET6;
828
+ addr.sin6_port = htons(port); // port=0 will get an ephemeral port
829
+
830
+ // Set bind address (default to all interfaces if empty)
831
+ if (bind_address.empty()) {
832
+ addr.sin6_addr = in6addr_any;
833
+ } else {
834
+ if (inet_pton(AF_INET6, bind_address.c_str(), &addr.sin6_addr) != 1) {
835
+ LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
836
+ close_socket(udp_socket);
837
+ return INVALID_SOCKET_VALUE;
838
+ }
839
+ }
840
+
841
+ if (bind(udp_socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR_VALUE) {
842
+ #ifdef _WIN32
843
+ LOG_SOCKET_ERROR("Failed to bind dual stack UDP socket to port " << port << " (error: " << WSAGetLastError() << ")");
844
+ #else
845
+ LOG_SOCKET_ERROR("Failed to bind dual stack UDP socket to port " << port << " (error: " << strerror(errno) << ")");
846
+ #endif
847
+ close_socket(udp_socket);
848
+ return INVALID_SOCKET_VALUE;
849
+ }
850
+
851
+ // Get the actual bound port if ephemeral port was requested
852
+ if (port == 0) {
853
+ sockaddr_in6 bound_addr;
854
+ socklen_t addr_len = sizeof(bound_addr);
855
+ if (getsockname(udp_socket, (struct sockaddr*)&bound_addr, &addr_len) == 0) {
856
+ uint16_t actual_port = ntohs(bound_addr.sin6_port);
857
+ LOG_SOCKET_INFO("Dual stack UDP socket bound to ephemeral port " << actual_port);
858
+ } else {
859
+ LOG_SOCKET_INFO("Dual stack UDP socket bound to ephemeral port (unknown)");
860
+ }
861
+ } else {
862
+ LOG_SOCKET_INFO("Dual stack UDP socket bound to port " << port);
863
+ }
864
+
865
+ return udp_socket;
866
+ }
867
+
868
+ socket_t create_udp_socket_v4(int port, const std::string& bind_address) {
869
+ LOG_SOCKET_DEBUG("Creating UDP socket on port " << port <<
870
+ (bind_address.empty() ? "" : " bound to " + bind_address));
871
+
872
+ // Validate port number
873
+ if (port < 0 || port > 65535) {
874
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
875
+ return INVALID_SOCKET_VALUE;
876
+ }
877
+
878
+ socket_t udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
879
+ if (udp_socket == INVALID_SOCKET_VALUE) {
880
+ #ifdef _WIN32
881
+ LOG_SOCKET_ERROR("Failed to create UDP socket (error: " << WSAGetLastError() << ")");
882
+ #else
883
+ LOG_SOCKET_ERROR("Failed to create UDP socket (error: " << strerror(errno) << ")");
884
+ #endif
885
+ return INVALID_SOCKET_VALUE;
886
+ }
887
+
888
+ // Set socket option to reuse address
889
+ int opt = 1;
890
+ if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR,
891
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
892
+ LOG_SOCKET_ERROR("Failed to set UDP socket options");
893
+ close_socket(udp_socket);
894
+ return INVALID_SOCKET_VALUE;
895
+ }
896
+
897
+ // Always bind the socket - this is required for receiving data
898
+ sockaddr_in addr;
899
+ memset(&addr, 0, sizeof(addr));
900
+ addr.sin_family = AF_INET;
901
+ addr.sin_port = htons(port); // port=0 will get an ephemeral port
902
+
903
+ // Set bind address (default to all interfaces if empty)
904
+ if (bind_address.empty()) {
905
+ addr.sin_addr.s_addr = INADDR_ANY;
906
+ } else {
907
+ if (inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr) != 1) {
908
+ LOG_SOCKET_ERROR("Invalid IPv4 bind address: " << bind_address);
909
+ close_socket(udp_socket);
910
+ return INVALID_SOCKET_VALUE;
911
+ }
912
+ }
913
+
914
+ if (bind(udp_socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR_VALUE) {
915
+ #ifdef _WIN32
916
+ LOG_SOCKET_ERROR("Failed to bind UDP socket to port " << port << " (error: " << WSAGetLastError() << ")");
917
+ #else
918
+ LOG_SOCKET_ERROR("Failed to bind UDP socket to port " << port << " (error: " << strerror(errno) << ")");
919
+ #endif
920
+ close_socket(udp_socket);
921
+ return INVALID_SOCKET_VALUE;
922
+ }
923
+
924
+ // Get the actual bound port if ephemeral port was requested
925
+ if (port == 0) {
926
+ sockaddr_in bound_addr;
927
+ socklen_t addr_len = sizeof(bound_addr);
928
+ if (getsockname(udp_socket, (struct sockaddr*)&bound_addr, &addr_len) == 0) {
929
+ uint16_t actual_port = ntohs(bound_addr.sin_port);
930
+ LOG_SOCKET_INFO("UDP socket bound to ephemeral port " << actual_port);
931
+ } else {
932
+ LOG_SOCKET_INFO("UDP socket bound to ephemeral port (unknown)");
933
+ }
934
+ } else {
935
+ LOG_SOCKET_INFO("UDP socket bound to port " << port);
936
+ }
937
+
938
+ return udp_socket;
939
+ }
940
+
941
+ socket_t create_udp_socket_v6(int port, const std::string& bind_address) {
942
+ LOG_SOCKET_DEBUG("Creating UDP socket on IPv6 port " << port <<
943
+ (bind_address.empty() ? "" : " bound to " + bind_address));
944
+
945
+ // Validate port number
946
+ if (port < 0 || port > 65535) {
947
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
948
+ return INVALID_SOCKET_VALUE;
949
+ }
950
+
951
+ socket_t udp_socket = socket(AF_INET6, SOCK_DGRAM, 0);
952
+ if (udp_socket == INVALID_SOCKET_VALUE) {
953
+ #ifdef _WIN32
954
+ LOG_SOCKET_ERROR("Failed to create IPv6 UDP socket (error: " << WSAGetLastError() << ")");
955
+ #else
956
+ LOG_SOCKET_ERROR("Failed to create IPv6 UDP socket (error: " << strerror(errno) << ")");
957
+ #endif
958
+ return INVALID_SOCKET_VALUE;
959
+ }
960
+
961
+ // Set socket option to reuse address
962
+ int opt = 1;
963
+ if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR,
964
+ (char*)&opt, sizeof(opt)) == SOCKET_ERROR_VALUE) {
965
+ LOG_SOCKET_ERROR("Failed to set IPv6 UDP socket options");
966
+ close_socket(udp_socket);
967
+ return INVALID_SOCKET_VALUE;
968
+ }
969
+
970
+ // Always bind the socket - this is required for receiving data
971
+ sockaddr_in6 addr;
972
+ memset(&addr, 0, sizeof(addr));
973
+ addr.sin6_family = AF_INET6;
974
+ addr.sin6_port = htons(port); // port=0 will get an ephemeral port
975
+
976
+ // Set bind address (default to all interfaces if empty)
977
+ if (bind_address.empty()) {
978
+ addr.sin6_addr = in6addr_any;
979
+ } else {
980
+ if (inet_pton(AF_INET6, bind_address.c_str(), &addr.sin6_addr) != 1) {
981
+ LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
982
+ close_socket(udp_socket);
983
+ return INVALID_SOCKET_VALUE;
984
+ }
985
+ }
986
+
987
+ if (bind(udp_socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR_VALUE) {
988
+ #ifdef _WIN32
989
+ LOG_SOCKET_ERROR("Failed to bind IPv6 UDP socket to port " << port << " (error: " << WSAGetLastError() << ")");
990
+ #else
991
+ LOG_SOCKET_ERROR("Failed to bind IPv6 UDP socket to port " << port << " (error: " << strerror(errno) << ")");
992
+ #endif
993
+ close_socket(udp_socket);
994
+ return INVALID_SOCKET_VALUE;
995
+ }
996
+
997
+ // Get the actual bound port if ephemeral port was requested
998
+ if (port == 0) {
999
+ sockaddr_in6 bound_addr;
1000
+ socklen_t addr_len = sizeof(bound_addr);
1001
+ if (getsockname(udp_socket, (struct sockaddr*)&bound_addr, &addr_len) == 0) {
1002
+ uint16_t actual_port = ntohs(bound_addr.sin6_port);
1003
+ LOG_SOCKET_INFO("IPv6 UDP socket bound to ephemeral port " << actual_port);
1004
+ } else {
1005
+ LOG_SOCKET_INFO("IPv6 UDP socket bound to ephemeral port (unknown)");
1006
+ }
1007
+ } else {
1008
+ LOG_SOCKET_INFO("IPv6 UDP socket bound to port " << port);
1009
+ }
1010
+
1011
+ return udp_socket;
1012
+ }
1013
+
1014
+
1015
+
1016
+ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data, const Peer& peer) {
1017
+ LOG_SOCKET_DEBUG("Sending " << data.size() << " bytes to " << peer.ip << ":" << peer.port);
1018
+
1019
+ // Check if it's an IPv6 address
1020
+ if (network_utils::is_valid_ipv6(peer.ip)) {
1021
+ // Handle IPv6 address
1022
+ sockaddr_in6 addr;
1023
+ memset(&addr, 0, sizeof(addr));
1024
+ addr.sin6_family = AF_INET6;
1025
+ addr.sin6_port = htons(peer.port);
1026
+
1027
+ if (inet_pton(AF_INET6, peer.ip.c_str(), &addr.sin6_addr) <= 0) {
1028
+ LOG_SOCKET_ERROR("Invalid IPv6 address: " << peer.ip);
1029
+ return -1;
1030
+ }
1031
+
1032
+ int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
1033
+ (struct sockaddr*)&addr, sizeof(addr));
1034
+ if (bytes_sent == SOCKET_ERROR_VALUE) {
1035
+ LOG_SOCKET_ERROR("Failed to send UDP data to IPv6 " << peer.ip << ":" << peer.port);
1036
+ return -1;
1037
+ }
1038
+
1039
+ LOG_SOCKET_DEBUG("Successfully sent " << bytes_sent << " bytes to IPv6 " << peer.ip << ":" << peer.port);
1040
+ return bytes_sent;
1041
+ } else {
1042
+ // Handle IPv4 address or hostname
1043
+ std::string resolved_ip = network_utils::resolve_hostname(peer.ip);
1044
+ if (resolved_ip.empty()) {
1045
+ LOG_SOCKET_ERROR("Failed to resolve hostname: " << peer.ip);
1046
+ return -1;
1047
+ }
1048
+
1049
+ // For dual-stack sockets, we need to use IPv6 address structure
1050
+ // and convert IPv4 to IPv4-mapped IPv6 address (::ffff:x.x.x.x)
1051
+ sockaddr_in6 addr;
1052
+ memset(&addr, 0, sizeof(addr));
1053
+ addr.sin6_family = AF_INET6;
1054
+ addr.sin6_port = htons(peer.port);
1055
+
1056
+ // Convert IPv4 to IPv4-mapped IPv6 address
1057
+ struct in_addr ipv4_addr;
1058
+ if (inet_pton(AF_INET, resolved_ip.c_str(), &ipv4_addr) <= 0) {
1059
+ LOG_SOCKET_ERROR("Invalid IPv4 address: " << resolved_ip);
1060
+ return -1;
1061
+ }
1062
+
1063
+ // Create IPv4-mapped IPv6 address: ::ffff:x.x.x.x
1064
+ addr.sin6_addr.s6_addr[10] = 0xff;
1065
+ addr.sin6_addr.s6_addr[11] = 0xff;
1066
+ memcpy(&addr.sin6_addr.s6_addr[12], &ipv4_addr.s_addr, 4);
1067
+
1068
+ LOG_SOCKET_DEBUG("Converting IPv4 " << resolved_ip << " to IPv4-mapped IPv6 for dual-stack socket");
1069
+
1070
+ int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
1071
+ (struct sockaddr*)&addr, sizeof(addr));
1072
+ if (bytes_sent == SOCKET_ERROR_VALUE) {
1073
+ #ifdef _WIN32
1074
+ LOG_SOCKET_ERROR("Failed to send UDP data to " << resolved_ip << ":" << peer.port << " (error: " << WSAGetLastError() << ")");
1075
+ #else
1076
+ LOG_SOCKET_ERROR("Failed to send UDP data to " << resolved_ip << ":" << peer.port << " (error: " << strerror(errno) << ")");
1077
+ #endif
1078
+ return -1;
1079
+ }
1080
+
1081
+ LOG_SOCKET_DEBUG("Successfully sent " << bytes_sent << " bytes to " << resolved_ip << ":" << peer.port << " via IPv4-mapped IPv6");
1082
+ return bytes_sent;
1083
+ }
1084
+ }
1085
+
1086
+ int send_udp_data_to(socket_t socket, const std::vector<uint8_t>& data, const std::string& hostname, int port) {
1087
+ LOG_SOCKET_DEBUG("Sending " << data.size() << " bytes to " << hostname << ":" << port);
1088
+
1089
+ // Validate port number
1090
+ if (port < 0 || port > 65535) {
1091
+ LOG_SOCKET_ERROR("Invalid port number: " << port << " (must be 0-65535)");
1092
+ return -1;
1093
+ }
1094
+
1095
+ // Resolve hostname to IP address
1096
+ std::string resolved_ip = network_utils::resolve_hostname(hostname);
1097
+ if (resolved_ip.empty()) {
1098
+ LOG_SOCKET_ERROR("Failed to resolve hostname: " << hostname);
1099
+ return -1;
1100
+ }
1101
+
1102
+ // Check if it's an IPv6 address
1103
+ if (network_utils::is_valid_ipv6(resolved_ip)) {
1104
+ // Handle IPv6 address
1105
+ sockaddr_in6 addr;
1106
+ memset(&addr, 0, sizeof(addr));
1107
+ addr.sin6_family = AF_INET6;
1108
+ addr.sin6_port = htons(port);
1109
+
1110
+ if (inet_pton(AF_INET6, resolved_ip.c_str(), &addr.sin6_addr) <= 0) {
1111
+ LOG_SOCKET_ERROR("Invalid IPv6 address: " << resolved_ip);
1112
+ return -1;
1113
+ }
1114
+
1115
+ int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
1116
+ (struct sockaddr*)&addr, sizeof(addr));
1117
+ if (bytes_sent == SOCKET_ERROR_VALUE) {
1118
+ LOG_SOCKET_ERROR("Failed to send UDP data to IPv6 " << resolved_ip << ":" << port);
1119
+ return -1;
1120
+ }
1121
+
1122
+ LOG_SOCKET_DEBUG("Successfully sent " << bytes_sent << " bytes to IPv6 " << resolved_ip << ":" << port);
1123
+ return bytes_sent;
1124
+ } else {
1125
+ // Handle IPv4 address
1126
+ sockaddr_in addr;
1127
+ memset(&addr, 0, sizeof(addr));
1128
+ addr.sin_family = AF_INET;
1129
+ addr.sin_port = htons(port);
1130
+
1131
+ if (inet_pton(AF_INET, resolved_ip.c_str(), &addr.sin_addr) <= 0) {
1132
+ LOG_SOCKET_ERROR("Invalid IPv4 address: " << resolved_ip);
1133
+ return -1;
1134
+ }
1135
+
1136
+ int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
1137
+ (struct sockaddr*)&addr, sizeof(addr));
1138
+ if (bytes_sent == SOCKET_ERROR_VALUE) {
1139
+ LOG_SOCKET_ERROR("Failed to send UDP data to " << resolved_ip << ":" << port);
1140
+ return -1;
1141
+ }
1142
+
1143
+ LOG_SOCKET_DEBUG("Successfully sent " << bytes_sent << " bytes to " << resolved_ip << ":" << port);
1144
+ return bytes_sent;
1145
+ }
1146
+ }
1147
+
1148
+ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer& sender_peer) {
1149
+ std::vector<uint8_t> buffer(buffer_size);
1150
+ sockaddr_storage sender_addr;
1151
+ socklen_t sender_addr_len = sizeof(sender_addr);
1152
+
1153
+ int bytes_received = recvfrom(socket, (char*)buffer.data(), buffer_size, 0,
1154
+ (struct sockaddr*)&sender_addr, &sender_addr_len);
1155
+
1156
+ if (bytes_received == SOCKET_ERROR_VALUE) {
1157
+ // Check if this is just a non-blocking socket with no data available
1158
+ #ifdef _WIN32
1159
+ int error = WSAGetLastError();
1160
+ if (error == WSAEWOULDBLOCK) {
1161
+ // No data available on non-blocking socket - this is normal
1162
+ return std::vector<uint8_t>();
1163
+ } else {
1164
+ LOG_SOCKET_ERROR("Failed to receive UDP data: " << error);
1165
+ return std::vector<uint8_t>();
1166
+ }
1167
+ #else
1168
+ int error = errno;
1169
+ if (error == EAGAIN || error == EWOULDBLOCK) {
1170
+ // No data available on non-blocking socket - this is normal
1171
+ return std::vector<uint8_t>();
1172
+ } else {
1173
+ LOG_SOCKET_ERROR("Failed to receive UDP data: " << strerror(error));
1174
+ return std::vector<uint8_t>();
1175
+ }
1176
+ #endif
1177
+ }
1178
+
1179
+ if (bytes_received == 0) {
1180
+ LOG_SOCKET_DEBUG("Received empty UDP packet");
1181
+ return std::vector<uint8_t>();
1182
+ }
1183
+
1184
+ // Extract sender information based on address family
1185
+ if (sender_addr.ss_family == AF_INET) {
1186
+ char sender_ip[INET_ADDRSTRLEN];
1187
+ struct sockaddr_in* addr_in = (struct sockaddr_in*)&sender_addr;
1188
+ inet_ntop(AF_INET, &addr_in->sin_addr, sender_ip, INET_ADDRSTRLEN);
1189
+ sender_peer.ip = sender_ip;
1190
+ sender_peer.port = ntohs(addr_in->sin_port);
1191
+
1192
+ LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from " << sender_peer.ip << ":" << sender_peer.port);
1193
+ } else if (sender_addr.ss_family == AF_INET6) {
1194
+ char sender_ip[INET6_ADDRSTRLEN];
1195
+ struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&sender_addr;
1196
+ inet_ntop(AF_INET6, &addr_in6->sin6_addr, sender_ip, INET6_ADDRSTRLEN);
1197
+ sender_peer.ip = sender_ip;
1198
+ sender_peer.port = ntohs(addr_in6->sin6_port);
1199
+
1200
+ LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from IPv6 [" << sender_peer.ip << "]:" << sender_peer.port);
1201
+ } else {
1202
+ LOG_SOCKET_WARN("Received UDP data from unknown address family");
1203
+ sender_peer.ip = "unknown";
1204
+ sender_peer.port = 0;
1205
+ }
1206
+
1207
+ buffer.resize(bytes_received);
1208
+ return buffer;
1209
+ }
1210
+
1211
+ std::vector<uint8_t> receive_udp_data_with_timeout(socket_t socket, size_t buffer_size, int timeout_ms,
1212
+ std::string* sender_ip, int* sender_port) {
1213
+ LOG_SOCKET_DEBUG("Receiving UDP data with timeout " << timeout_ms << "ms");
1214
+
1215
+ std::vector<uint8_t> buffer(buffer_size);
1216
+ sockaddr_storage sender_addr;
1217
+ socklen_t sender_addr_len = sizeof(sender_addr);
1218
+
1219
+ // Handle timeout if specified
1220
+ if (timeout_ms > 0) {
1221
+ // Set up timeout using select
1222
+ fd_set read_fds;
1223
+ FD_ZERO(&read_fds);
1224
+ FD_SET(socket, &read_fds);
1225
+
1226
+ struct timeval timeout;
1227
+ timeout.tv_sec = timeout_ms / 1000;
1228
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
1229
+
1230
+ // Wait for data with timeout
1231
+ int result = select(socket + 1, &read_fds, nullptr, nullptr, &timeout);
1232
+ if (result == 0) {
1233
+ LOG_SOCKET_DEBUG("UDP receive timeout (" << timeout_ms << "ms)");
1234
+ return std::vector<uint8_t>();
1235
+ } else if (result < 0) {
1236
+ LOG_SOCKET_ERROR("Select error while waiting for UDP data");
1237
+ return std::vector<uint8_t>();
1238
+ }
1239
+ }
1240
+
1241
+ int bytes_received = recvfrom(socket, (char*)buffer.data(), buffer_size, 0,
1242
+ (struct sockaddr*)&sender_addr, &sender_addr_len);
1243
+
1244
+ if (bytes_received == SOCKET_ERROR_VALUE) {
1245
+ // Check if this is just a non-blocking socket with no data available
1246
+ #ifdef _WIN32
1247
+ int error = WSAGetLastError();
1248
+ if (error == WSAEWOULDBLOCK) {
1249
+ // No data available on non-blocking socket - this is normal
1250
+ LOG_SOCKET_DEBUG("No UDP data available (non-blocking)");
1251
+ return std::vector<uint8_t>();
1252
+ } else {
1253
+ LOG_SOCKET_ERROR("Failed to receive UDP data: " << error);
1254
+ return std::vector<uint8_t>();
1255
+ }
1256
+ #else
1257
+ int error = errno;
1258
+ if (error == EAGAIN || error == EWOULDBLOCK) {
1259
+ // No data available on non-blocking socket - this is normal
1260
+ LOG_SOCKET_DEBUG("No UDP data available (non-blocking)");
1261
+ return std::vector<uint8_t>();
1262
+ } else {
1263
+ LOG_SOCKET_ERROR("Failed to receive UDP data: " << strerror(error));
1264
+ return std::vector<uint8_t>();
1265
+ }
1266
+ #endif
1267
+ }
1268
+
1269
+ if (bytes_received == 0) {
1270
+ LOG_SOCKET_DEBUG("Received empty UDP packet");
1271
+ return std::vector<uint8_t>();
1272
+ }
1273
+
1274
+ // Extract sender information based on address family
1275
+ if (sender_addr.ss_family == AF_INET) {
1276
+ char ip_str[INET_ADDRSTRLEN];
1277
+ struct sockaddr_in* addr_in = (struct sockaddr_in*)&sender_addr;
1278
+ inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
1279
+
1280
+ if (sender_ip) *sender_ip = ip_str;
1281
+ if (sender_port) *sender_port = ntohs(addr_in->sin_port);
1282
+
1283
+ LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from " << ip_str << ":" << ntohs(addr_in->sin_port));
1284
+ } else if (sender_addr.ss_family == AF_INET6) {
1285
+ char ip_str[INET6_ADDRSTRLEN];
1286
+ struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&sender_addr;
1287
+ inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
1288
+
1289
+ if (sender_ip) *sender_ip = ip_str;
1290
+ if (sender_port) *sender_port = ntohs(addr_in6->sin6_port);
1291
+
1292
+ LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from IPv6 [" << ip_str << "]:" << ntohs(addr_in6->sin6_port));
1293
+ } else {
1294
+ LOG_SOCKET_WARN("Received UDP data from unknown address family");
1295
+ if (sender_ip) *sender_ip = "unknown";
1296
+ if (sender_port) *sender_port = 0;
1297
+ }
1298
+
1299
+ buffer.resize(bytes_received);
1300
+ return buffer;
1301
+ }
1302
+
1303
+ // Helper function to determine if a socket is TCP
1304
+ bool is_tcp_socket(socket_t socket) {
1305
+ if (!is_valid_socket(socket)) {
1306
+ return false;
1307
+ }
1308
+
1309
+ int sock_type;
1310
+ socklen_t opt_len = sizeof(sock_type);
1311
+
1312
+ if (getsockopt(socket, SOL_SOCKET, SO_TYPE, (char*)&sock_type, &opt_len) == 0) {
1313
+ return sock_type == SOCK_STREAM;
1314
+ }
1315
+
1316
+ return false; // Assume not TCP if we can't determine
1317
+ }
1318
+
1319
+ // Common Socket Functions
1320
+ void close_socket(socket_t socket, bool force) {
1321
+ if (is_valid_socket(socket)) {
1322
+ LOG_SOCKET_DEBUG("Closing socket " << socket);
1323
+
1324
+ // Peform force shutdown if needed
1325
+ if (force) {
1326
+ LOG_SOCKET_DEBUG("Performing shutdown for TCP socket " << socket);
1327
+ #ifdef _WIN32
1328
+ shutdown(socket, SD_BOTH);
1329
+ #else
1330
+ shutdown(socket, SHUT_RDWR);
1331
+ #endif
1332
+ }
1333
+
1334
+ closesocket(socket);
1335
+ }
1336
+ }
1337
+
1338
+ bool is_valid_socket(socket_t socket) {
1339
+ return socket != INVALID_SOCKET_VALUE;
1340
+ }
1341
+
1342
+ bool set_socket_nonblocking(socket_t socket) {
1343
+ #ifdef _WIN32
1344
+ unsigned long mode = 1;
1345
+ if (ioctlsocket(socket, FIONBIO, &mode) != 0) {
1346
+ LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode");
1347
+ return false;
1348
+ }
1349
+ #else
1350
+ int flags = fcntl(socket, F_GETFL, 0);
1351
+ if (flags == -1) {
1352
+ LOG_SOCKET_ERROR("Failed to get socket flags");
1353
+ return false;
1354
+ }
1355
+
1356
+ if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
1357
+ LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode");
1358
+ return false;
1359
+ }
1360
+ #endif
1361
+
1362
+ LOG_SOCKET_DEBUG("Socket set to non-blocking mode");
1363
+ return true;
1364
+ }
1365
+
1366
+ int get_ephemeral_port(socket_t socket)
1367
+ {
1368
+ sockaddr_in6 bound_addr;
1369
+ socklen_t addr_len = sizeof(bound_addr);
1370
+ if (getsockname(socket, (struct sockaddr*)&bound_addr, &addr_len) == 0) {
1371
+ return ntohs(bound_addr.sin6_port);
1372
+ }
1373
+ return 0;
1374
+ }
1375
+
1376
+ } // namespace librats