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
|
@@ -35,350 +35,9 @@
|
|
|
35
35
|
namespace librats {
|
|
36
36
|
namespace network_utils {
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
LOG_NETUTILS_DEBUG("Resolving hostname: " << hostname);
|
|
40
|
-
|
|
41
|
-
// Handle empty string
|
|
42
|
-
if (hostname.empty()) {
|
|
43
|
-
LOG_NETUTILS_DEBUG("Empty hostname provided");
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Check if it's already an IP address
|
|
48
|
-
if (is_valid_ipv4(hostname)) {
|
|
49
|
-
LOG_NETUTILS_DEBUG("Already an IP address: " << hostname);
|
|
50
|
-
return hostname;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Resolve hostname using getaddrinfo
|
|
54
|
-
struct addrinfo hints, *result;
|
|
55
|
-
memset(&hints, 0, sizeof(hints));
|
|
56
|
-
hints.ai_family = AF_INET; // IPv4
|
|
57
|
-
hints.ai_socktype = SOCK_DGRAM; // UDP
|
|
58
|
-
|
|
59
|
-
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
60
|
-
if (status != 0) {
|
|
61
|
-
#ifdef _WIN32
|
|
62
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << WSAGetLastError());
|
|
63
|
-
#else
|
|
64
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << gai_strerror(status));
|
|
65
|
-
#endif
|
|
66
|
-
return "";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
char ip_str[INET_ADDRSTRLEN];
|
|
70
|
-
struct sockaddr_in* addr_in = (struct sockaddr_in*)result->ai_addr;
|
|
71
|
-
inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
|
|
72
|
-
|
|
73
|
-
freeaddrinfo(result);
|
|
74
|
-
|
|
75
|
-
LOG_NETUTILS_INFO("Resolved " << hostname << " to " << ip_str);
|
|
76
|
-
return std::string(ip_str);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
std::string resolve_hostname_v6(const std::string& hostname) {
|
|
80
|
-
LOG_NETUTILS_DEBUG("Resolving hostname to IPv6: " << hostname);
|
|
81
|
-
|
|
82
|
-
// Handle empty string
|
|
83
|
-
if (hostname.empty()) {
|
|
84
|
-
LOG_NETUTILS_DEBUG("Empty hostname provided");
|
|
85
|
-
return "";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check if it's already an IPv6 address
|
|
89
|
-
if (is_valid_ipv6(hostname)) {
|
|
90
|
-
LOG_NETUTILS_DEBUG("Already an IPv6 address: " << hostname);
|
|
91
|
-
return hostname;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Resolve hostname using getaddrinfo
|
|
95
|
-
struct addrinfo hints, *result;
|
|
96
|
-
memset(&hints, 0, sizeof(hints));
|
|
97
|
-
hints.ai_family = AF_INET6; // IPv6
|
|
98
|
-
hints.ai_socktype = SOCK_DGRAM; // UDP
|
|
99
|
-
|
|
100
|
-
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
101
|
-
if (status != 0) {
|
|
102
|
-
#ifdef _WIN32
|
|
103
|
-
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << WSAGetLastError());
|
|
104
|
-
#else
|
|
105
|
-
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << gai_strerror(status));
|
|
106
|
-
#endif
|
|
107
|
-
return "";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
char ip_str[INET6_ADDRSTRLEN];
|
|
111
|
-
struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)result->ai_addr;
|
|
112
|
-
inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
|
|
113
|
-
|
|
114
|
-
freeaddrinfo(result);
|
|
115
|
-
|
|
116
|
-
LOG_NETUTILS_DEBUG("Resolved " << hostname << " to IPv6 " << ip_str);
|
|
117
|
-
return std::string(ip_str);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
bool is_valid_ipv4(const std::string& ip_str) {
|
|
121
|
-
#ifdef __APPLE__
|
|
122
|
-
// macOS-specific validation to handle inet_pton() accepting leading zeros
|
|
123
|
-
// inet_pton() behaves differently on macOS vs Linux/Windows for leading zeros
|
|
124
|
-
|
|
125
|
-
// Split the IP string into octets
|
|
126
|
-
std::vector<std::string> octets;
|
|
127
|
-
std::string current_octet;
|
|
128
|
-
|
|
129
|
-
for (char c : ip_str) {
|
|
130
|
-
if (c == '.') {
|
|
131
|
-
octets.push_back(current_octet);
|
|
132
|
-
current_octet.clear();
|
|
133
|
-
} else {
|
|
134
|
-
current_octet += c;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Add the last octet
|
|
139
|
-
if (!current_octet.empty()) {
|
|
140
|
-
octets.push_back(current_octet);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Must have exactly 4 octets
|
|
144
|
-
if (octets.size() != 4) {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Check each octet for validity
|
|
149
|
-
for (const std::string& octet : octets) {
|
|
150
|
-
// Empty octet is invalid
|
|
151
|
-
if (octet.empty()) {
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Check for leading zeros (invalid except for "0")
|
|
156
|
-
if (octet.length() > 1 && octet[0] == '0') {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Check that all characters are digits
|
|
161
|
-
for (char c : octet) {
|
|
162
|
-
if (c < '0' || c > '9') {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Check range (0-255)
|
|
168
|
-
int value = std::stoi(octet);
|
|
169
|
-
if (value < 0 || value > 255) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
#endif
|
|
174
|
-
// Linux/Windows: inet_pton() already rejects leading zeros correctly
|
|
175
|
-
struct sockaddr_in sa;
|
|
176
|
-
return inet_pton(AF_INET, ip_str.c_str(), &sa.sin_addr) == 1;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
bool is_valid_ipv6(const std::string& ip_str) {
|
|
180
|
-
struct sockaddr_in6 sa;
|
|
181
|
-
return inet_pton(AF_INET6, ip_str.c_str(), &sa.sin6_addr) == 1;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
bool is_hostname(const std::string& str) {
|
|
185
|
-
// First check if it's an IP address - if so, it's not a hostname
|
|
186
|
-
if (is_valid_ipv4(str) || is_valid_ipv6(str)) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Check basic validation rules
|
|
191
|
-
if (str.empty() || str.length() > 253) {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Check for invalid characters at start/end
|
|
196
|
-
if (str.front() == '.' || str.back() == '.') {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (str.front() == '-' || str.back() == '-') {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Check for invalid patterns
|
|
205
|
-
if (str.find("..") != std::string::npos) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (str == ".") {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Check for invalid characters
|
|
214
|
-
for (char c : str) {
|
|
215
|
-
if (c == ' ' || c == '@' || c == '#' || c == '$' || c == '%' ||
|
|
216
|
-
c == '^' || c == '&' || c == '*' || c == '(' || c == ')' ||
|
|
217
|
-
c == '+' || c == '=' || c == '[' || c == ']' || c == '{' ||
|
|
218
|
-
c == '}' || c == '|' || c == '\\' || c == '/' || c == '?' ||
|
|
219
|
-
c == '<' || c == '>' || c == ',' || c == ';' || c == ':' ||
|
|
220
|
-
c == '"' || c == '\'' || c == '`' || c == '~' || c == '!') {
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
std::string to_ip_address(const std::string& host) {
|
|
229
|
-
return resolve_hostname(host);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
std::vector<std::string> resolve_all_addresses(const std::string& hostname) {
|
|
233
|
-
LOG_NETUTILS_DEBUG("Resolving all addresses for hostname: " << hostname);
|
|
234
|
-
|
|
235
|
-
std::vector<std::string> addresses;
|
|
236
|
-
|
|
237
|
-
// Check if it's already an IP address
|
|
238
|
-
if (is_valid_ipv4(hostname)) {
|
|
239
|
-
LOG_NETUTILS_DEBUG("Already an IP address: " << hostname);
|
|
240
|
-
addresses.push_back(hostname);
|
|
241
|
-
return addresses;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Resolve hostname using getaddrinfo
|
|
245
|
-
struct addrinfo hints, *result, *rp;
|
|
246
|
-
memset(&hints, 0, sizeof(hints));
|
|
247
|
-
hints.ai_family = AF_INET; // IPv4
|
|
248
|
-
hints.ai_socktype = SOCK_DGRAM; // UDP
|
|
249
|
-
|
|
250
|
-
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
251
|
-
if (status != 0) {
|
|
252
|
-
#ifdef _WIN32
|
|
253
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << WSAGetLastError());
|
|
254
|
-
#else
|
|
255
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << gai_strerror(status));
|
|
256
|
-
#endif
|
|
257
|
-
return addresses;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Iterate through all addresses
|
|
261
|
-
for (rp = result; rp != nullptr; rp = rp->ai_next) {
|
|
262
|
-
char ip_str[INET_ADDRSTRLEN];
|
|
263
|
-
struct sockaddr_in* addr_in = (struct sockaddr_in*)rp->ai_addr;
|
|
264
|
-
inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
|
|
265
|
-
|
|
266
|
-
std::string ip_address(ip_str);
|
|
267
|
-
addresses.push_back(ip_address);
|
|
268
|
-
LOG_NETUTILS_DEBUG("Found address: " << ip_address);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
freeaddrinfo(result);
|
|
272
|
-
|
|
273
|
-
LOG_NETUTILS_INFO("Resolved " << hostname << " to " << addresses.size() << " addresses");
|
|
274
|
-
return addresses;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
std::vector<std::string> resolve_all_addresses_v6(const std::string& hostname) {
|
|
278
|
-
LOG_NETUTILS_DEBUG("Resolving all IPv6 addresses for hostname: " << hostname);
|
|
279
|
-
|
|
280
|
-
std::vector<std::string> addresses;
|
|
281
|
-
|
|
282
|
-
// Check if it's already an IPv6 address
|
|
283
|
-
if (is_valid_ipv6(hostname)) {
|
|
284
|
-
LOG_NETUTILS_DEBUG("Already an IPv6 address: " << hostname);
|
|
285
|
-
addresses.push_back(hostname);
|
|
286
|
-
return addresses;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Resolve hostname using getaddrinfo
|
|
290
|
-
struct addrinfo hints, *result, *rp;
|
|
291
|
-
memset(&hints, 0, sizeof(hints));
|
|
292
|
-
hints.ai_family = AF_INET6; // IPv6
|
|
293
|
-
hints.ai_socktype = SOCK_DGRAM; // UDP
|
|
294
|
-
|
|
295
|
-
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
296
|
-
if (status != 0) {
|
|
297
|
-
#ifdef _WIN32
|
|
298
|
-
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << WSAGetLastError());
|
|
299
|
-
#else
|
|
300
|
-
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << gai_strerror(status));
|
|
301
|
-
#endif
|
|
302
|
-
return addresses;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Iterate through all addresses
|
|
306
|
-
for (rp = result; rp != nullptr; rp = rp->ai_next) {
|
|
307
|
-
char ip_str[INET6_ADDRSTRLEN];
|
|
308
|
-
struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)rp->ai_addr;
|
|
309
|
-
inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
|
|
310
|
-
|
|
311
|
-
std::string ip_address(ip_str);
|
|
312
|
-
addresses.push_back(ip_address);
|
|
313
|
-
LOG_NETUTILS_DEBUG("Found IPv6 address: " << ip_address);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
freeaddrinfo(result);
|
|
317
|
-
|
|
318
|
-
LOG_NETUTILS_INFO("Resolved " << hostname << " to " << addresses.size() << " IPv6 addresses");
|
|
319
|
-
return addresses;
|
|
320
|
-
}
|
|
38
|
+
// ── Internal helpers (not exposed in header) ────────────────────────────────
|
|
321
39
|
|
|
322
|
-
|
|
323
|
-
LOG_NETUTILS_DEBUG("Resolving all addresses (dual stack) for hostname: " << hostname);
|
|
324
|
-
|
|
325
|
-
std::vector<std::string> addresses;
|
|
326
|
-
|
|
327
|
-
// Check if it's already an IP address
|
|
328
|
-
if (is_valid_ipv4(hostname)) {
|
|
329
|
-
LOG_NETUTILS_DEBUG("Already an IPv4 address: " << hostname);
|
|
330
|
-
addresses.push_back(hostname);
|
|
331
|
-
return addresses;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (is_valid_ipv6(hostname)) {
|
|
335
|
-
LOG_NETUTILS_DEBUG("Already an IPv6 address: " << hostname);
|
|
336
|
-
addresses.push_back(hostname);
|
|
337
|
-
return addresses;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Resolve hostname using getaddrinfo with dual stack
|
|
341
|
-
struct addrinfo hints, *result, *rp;
|
|
342
|
-
memset(&hints, 0, sizeof(hints));
|
|
343
|
-
hints.ai_family = AF_UNSPEC; // Both IPv4 and IPv6
|
|
344
|
-
hints.ai_socktype = SOCK_DGRAM; // UDP
|
|
345
|
-
|
|
346
|
-
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
347
|
-
if (status != 0) {
|
|
348
|
-
#ifdef _WIN32
|
|
349
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << " (dual stack): " << WSAGetLastError());
|
|
350
|
-
#else
|
|
351
|
-
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << " (dual stack): " << gai_strerror(status));
|
|
352
|
-
#endif
|
|
353
|
-
return addresses;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Iterate through all addresses
|
|
357
|
-
for (rp = result; rp != nullptr; rp = rp->ai_next) {
|
|
358
|
-
if (rp->ai_family == AF_INET) {
|
|
359
|
-
char ip_str[INET_ADDRSTRLEN];
|
|
360
|
-
struct sockaddr_in* addr_in = (struct sockaddr_in*)rp->ai_addr;
|
|
361
|
-
inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
|
|
362
|
-
|
|
363
|
-
std::string ip_address(ip_str);
|
|
364
|
-
addresses.push_back(ip_address);
|
|
365
|
-
LOG_NETUTILS_DEBUG("Found IPv4 address: " << ip_address);
|
|
366
|
-
} else if (rp->ai_family == AF_INET6) {
|
|
367
|
-
char ip_str[INET6_ADDRSTRLEN];
|
|
368
|
-
struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)rp->ai_addr;
|
|
369
|
-
inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
|
|
370
|
-
|
|
371
|
-
std::string ip_address(ip_str);
|
|
372
|
-
addresses.push_back(ip_address);
|
|
373
|
-
LOG_NETUTILS_DEBUG("Found IPv6 address: " << ip_address);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
freeaddrinfo(result);
|
|
378
|
-
|
|
379
|
-
LOG_NETUTILS_INFO("Resolved " << hostname << " to " << addresses.size() << " addresses (dual stack)");
|
|
380
|
-
return addresses;
|
|
381
|
-
}
|
|
40
|
+
namespace {
|
|
382
41
|
|
|
383
42
|
std::vector<std::string> get_local_interface_addresses_v4() {
|
|
384
43
|
LOG_NETUTILS_DEBUG("Getting local IPv4 interface addresses");
|
|
@@ -386,7 +45,6 @@ std::vector<std::string> get_local_interface_addresses_v4() {
|
|
|
386
45
|
std::vector<std::string> addresses;
|
|
387
46
|
|
|
388
47
|
#ifdef _WIN32
|
|
389
|
-
// Windows implementation using GetAdaptersAddresses
|
|
390
48
|
DWORD dwRetVal = 0;
|
|
391
49
|
ULONG outBufLen = 15000;
|
|
392
50
|
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
@@ -438,7 +96,6 @@ std::vector<std::string> get_local_interface_addresses_v4() {
|
|
|
438
96
|
}
|
|
439
97
|
|
|
440
98
|
#else
|
|
441
|
-
// Linux/Unix implementation using getifaddrs
|
|
442
99
|
struct ifaddrs *ifaddr, *ifa;
|
|
443
100
|
|
|
444
101
|
if (getifaddrs(&ifaddr) == -1) {
|
|
@@ -472,7 +129,6 @@ std::vector<std::string> get_local_interface_addresses_v6() {
|
|
|
472
129
|
std::vector<std::string> addresses;
|
|
473
130
|
|
|
474
131
|
#ifdef _WIN32
|
|
475
|
-
// Windows implementation using GetAdaptersAddresses
|
|
476
132
|
DWORD dwRetVal = 0;
|
|
477
133
|
ULONG outBufLen = 15000;
|
|
478
134
|
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
@@ -524,7 +180,6 @@ std::vector<std::string> get_local_interface_addresses_v6() {
|
|
|
524
180
|
}
|
|
525
181
|
|
|
526
182
|
#else
|
|
527
|
-
// Linux/Unix implementation using getifaddrs
|
|
528
183
|
struct ifaddrs *ifaddr, *ifa;
|
|
529
184
|
|
|
530
185
|
if (getifaddrs(&ifaddr) == -1) {
|
|
@@ -552,16 +207,186 @@ std::vector<std::string> get_local_interface_addresses_v6() {
|
|
|
552
207
|
return addresses;
|
|
553
208
|
}
|
|
554
209
|
|
|
210
|
+
} // anonymous namespace
|
|
211
|
+
|
|
212
|
+
// ── Public API ──────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
std::string resolve_hostname(const std::string& hostname) {
|
|
215
|
+
LOG_NETUTILS_DEBUG("Resolving hostname: " << hostname);
|
|
216
|
+
|
|
217
|
+
if (hostname.empty()) {
|
|
218
|
+
LOG_NETUTILS_DEBUG("Empty hostname provided");
|
|
219
|
+
return "";
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (is_valid_ipv4(hostname)) {
|
|
223
|
+
LOG_NETUTILS_DEBUG("Already an IP address: " << hostname);
|
|
224
|
+
return hostname;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
struct addrinfo hints, *result;
|
|
228
|
+
memset(&hints, 0, sizeof(hints));
|
|
229
|
+
hints.ai_family = AF_INET;
|
|
230
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
231
|
+
|
|
232
|
+
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
233
|
+
if (status != 0) {
|
|
234
|
+
#ifdef _WIN32
|
|
235
|
+
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << WSAGetLastError());
|
|
236
|
+
#else
|
|
237
|
+
LOG_NETUTILS_ERROR("Failed to resolve hostname " << hostname << ": " << gai_strerror(status));
|
|
238
|
+
#endif
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
char ip_str[INET_ADDRSTRLEN];
|
|
243
|
+
struct sockaddr_in* addr_in = (struct sockaddr_in*)result->ai_addr;
|
|
244
|
+
inet_ntop(AF_INET, &addr_in->sin_addr, ip_str, INET_ADDRSTRLEN);
|
|
245
|
+
|
|
246
|
+
freeaddrinfo(result);
|
|
247
|
+
|
|
248
|
+
LOG_NETUTILS_INFO("Resolved " << hostname << " to " << ip_str);
|
|
249
|
+
return std::string(ip_str);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
std::string resolve_hostname_v6(const std::string& hostname) {
|
|
253
|
+
LOG_NETUTILS_DEBUG("Resolving hostname to IPv6: " << hostname);
|
|
254
|
+
|
|
255
|
+
if (hostname.empty()) {
|
|
256
|
+
LOG_NETUTILS_DEBUG("Empty hostname provided");
|
|
257
|
+
return "";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (is_valid_ipv6(hostname)) {
|
|
261
|
+
LOG_NETUTILS_DEBUG("Already an IPv6 address: " << hostname);
|
|
262
|
+
return hostname;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
struct addrinfo hints, *result;
|
|
266
|
+
memset(&hints, 0, sizeof(hints));
|
|
267
|
+
hints.ai_family = AF_INET6;
|
|
268
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
269
|
+
|
|
270
|
+
int status = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
|
|
271
|
+
if (status != 0) {
|
|
272
|
+
#ifdef _WIN32
|
|
273
|
+
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << WSAGetLastError());
|
|
274
|
+
#else
|
|
275
|
+
LOG_NETUTILS_DEBUG("Failed to resolve hostname " << hostname << " to IPv6: " << gai_strerror(status));
|
|
276
|
+
#endif
|
|
277
|
+
return "";
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
char ip_str[INET6_ADDRSTRLEN];
|
|
281
|
+
struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)result->ai_addr;
|
|
282
|
+
inet_ntop(AF_INET6, &addr_in6->sin6_addr, ip_str, INET6_ADDRSTRLEN);
|
|
283
|
+
|
|
284
|
+
freeaddrinfo(result);
|
|
285
|
+
|
|
286
|
+
LOG_NETUTILS_DEBUG("Resolved " << hostname << " to IPv6 " << ip_str);
|
|
287
|
+
return std::string(ip_str);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
bool is_valid_ipv4(const std::string& ip_str) {
|
|
291
|
+
#ifdef __APPLE__
|
|
292
|
+
// macOS-specific validation to handle inet_pton() accepting leading zeros
|
|
293
|
+
std::vector<std::string> octets;
|
|
294
|
+
std::string current_octet;
|
|
295
|
+
|
|
296
|
+
for (char c : ip_str) {
|
|
297
|
+
if (c == '.') {
|
|
298
|
+
octets.push_back(current_octet);
|
|
299
|
+
current_octet.clear();
|
|
300
|
+
} else {
|
|
301
|
+
current_octet += c;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!current_octet.empty()) {
|
|
306
|
+
octets.push_back(current_octet);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (octets.size() != 4) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for (const std::string& octet : octets) {
|
|
314
|
+
if (octet.empty()) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (octet.length() > 1 && octet[0] == '0') {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
for (char c : octet) {
|
|
323
|
+
if (c < '0' || c > '9') {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
int value = std::stoi(octet);
|
|
329
|
+
if (value < 0 || value > 255) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
#endif
|
|
334
|
+
struct sockaddr_in sa;
|
|
335
|
+
return inet_pton(AF_INET, ip_str.c_str(), &sa.sin_addr) == 1;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
bool is_valid_ipv6(const std::string& ip_str) {
|
|
339
|
+
struct sockaddr_in6 sa;
|
|
340
|
+
return inet_pton(AF_INET6, ip_str.c_str(), &sa.sin6_addr) == 1;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
bool is_hostname(const std::string& str) {
|
|
344
|
+
if (is_valid_ipv4(str) || is_valid_ipv6(str)) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (str.empty() || str.length() > 253) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (str.front() == '.' || str.back() == '.') {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (str.front() == '-' || str.back() == '-') {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (str.find("..") != std::string::npos) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (str == ".") {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
for (char c : str) {
|
|
369
|
+
if (c == ' ' || c == '@' || c == '#' || c == '$' || c == '%' ||
|
|
370
|
+
c == '^' || c == '&' || c == '*' || c == '(' || c == ')' ||
|
|
371
|
+
c == '+' || c == '=' || c == '[' || c == ']' || c == '{' ||
|
|
372
|
+
c == '}' || c == '|' || c == '\\' || c == '/' || c == '?' ||
|
|
373
|
+
c == '<' || c == '>' || c == ',' || c == ';' || c == ':' ||
|
|
374
|
+
c == '"' || c == '\'' || c == '`' || c == '~' || c == '!') {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
555
382
|
std::vector<std::string> get_local_interface_addresses() {
|
|
556
383
|
LOG_NETUTILS_DEBUG("Getting all local interface addresses (IPv4 and IPv6)");
|
|
557
384
|
|
|
558
385
|
std::vector<std::string> addresses;
|
|
559
386
|
|
|
560
|
-
// Get IPv4 addresses
|
|
561
387
|
auto ipv4_addresses = get_local_interface_addresses_v4();
|
|
562
388
|
addresses.insert(addresses.end(), ipv4_addresses.begin(), ipv4_addresses.end());
|
|
563
389
|
|
|
564
|
-
// Get IPv6 addresses
|
|
565
390
|
auto ipv6_addresses = get_local_interface_addresses_v6();
|
|
566
391
|
addresses.insert(addresses.end(), ipv6_addresses.begin(), ipv6_addresses.end());
|
|
567
392
|
|
|
@@ -571,28 +396,5 @@ std::vector<std::string> get_local_interface_addresses() {
|
|
|
571
396
|
return addresses;
|
|
572
397
|
}
|
|
573
398
|
|
|
574
|
-
bool is_local_interface_address(const std::string& ip_address) {
|
|
575
|
-
LOG_NETUTILS_DEBUG("Checking if " << ip_address << " is a local interface address");
|
|
576
|
-
|
|
577
|
-
// Get all local addresses and check if the given address is in the list
|
|
578
|
-
auto local_addresses = get_local_interface_addresses();
|
|
579
|
-
|
|
580
|
-
for (const auto& local_addr : local_addresses) {
|
|
581
|
-
if (local_addr == ip_address) {
|
|
582
|
-
LOG_NETUTILS_DEBUG(ip_address << " is a local interface address");
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Also check common localhost addresses
|
|
588
|
-
if (ip_address == "127.0.0.1" || ip_address == "::1" || ip_address == "localhost") {
|
|
589
|
-
LOG_NETUTILS_DEBUG(ip_address << " is a localhost address");
|
|
590
|
-
return true;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
LOG_NETUTILS_DEBUG(ip_address << " is not a local interface address");
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
399
|
} // namespace network_utils
|
|
598
|
-
} // namespace librats
|
|
400
|
+
} // namespace librats
|