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