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.
- package/README.md +1 -1
- package/binding.gyp +1 -0
- package/lib/index.d.ts +2 -1
- 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 +2460 -0
- package/native-src/src/dht.h +508 -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 +2735 -0
- package/native-src/src/librats.h +1732 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1333 -0
- package/native-src/src/librats_c.h +239 -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
- 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
|