librats 0.9.0 → 0.9.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/native-src/CMakeLists.txt +10 -0
- package/native-src/src/dht.cpp +3 -32
- package/native-src/src/dht.h +9 -1
- package/native-src/src/librats.cpp +10 -2
- package/native-src/src/librats.h +73 -1
- package/native-src/src/librats_discovery.cpp +22 -3
- package/native-src/src/librats_persistence.cpp +12 -2
- package/native-src/src/librats_portmap.cpp +244 -0
- package/native-src/src/natpmp.cpp +416 -0
- package/native-src/src/natpmp.h +140 -0
- package/native-src/src/network_utils.cpp +183 -2
- package/native-src/src/network_utils.h +27 -0
- package/native-src/src/port_mapping.h +78 -0
- package/native-src/src/socket.cpp +26 -6
- package/native-src/src/socket.h +6 -2
- package/native-src/src/upnp.cpp +635 -0
- package/native-src/src/upnp.h +163 -0
- package/native-src/src/wakeup_pipe.h +60 -0
- package/package.json +1 -1
|
@@ -16,6 +16,17 @@
|
|
|
16
16
|
#else
|
|
17
17
|
#include <ifaddrs.h>
|
|
18
18
|
#endif
|
|
19
|
+
|
|
20
|
+
// macOS / BSD default-gateway lookup via the PF_ROUTE sysctl routing table.
|
|
21
|
+
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
|
|
22
|
+
defined(__OpenBSD__) || defined(__DragonFly__)
|
|
23
|
+
#define RATS_HAVE_BSD_ROUTES 1
|
|
24
|
+
#include <sys/types.h>
|
|
25
|
+
#include <sys/socket.h>
|
|
26
|
+
#include <sys/sysctl.h>
|
|
27
|
+
#include <net/route.h>
|
|
28
|
+
#include <net/if.h>
|
|
29
|
+
#endif
|
|
19
30
|
#endif
|
|
20
31
|
|
|
21
32
|
|
|
@@ -25,6 +36,12 @@
|
|
|
25
36
|
#include <cstring>
|
|
26
37
|
#include <iostream>
|
|
27
38
|
#include <vector>
|
|
39
|
+
#include <algorithm>
|
|
40
|
+
|
|
41
|
+
#ifndef _WIN32
|
|
42
|
+
#include <cstdio>
|
|
43
|
+
#include <cstdlib>
|
|
44
|
+
#endif
|
|
28
45
|
|
|
29
46
|
// Network utilities module logging macros
|
|
30
47
|
#define LOG_NETUTILS_DEBUG(message) LOG_DEBUG("network_utils", message)
|
|
@@ -379,6 +396,41 @@ bool is_hostname(const std::string& str) {
|
|
|
379
396
|
return true;
|
|
380
397
|
}
|
|
381
398
|
|
|
399
|
+
bool is_public_ip(const std::string& ip) {
|
|
400
|
+
if (ip.empty()) return false;
|
|
401
|
+
|
|
402
|
+
if (is_valid_ipv6(ip)) {
|
|
403
|
+
struct in6_addr a;
|
|
404
|
+
if (inet_pton(AF_INET6, ip.c_str(), &a) != 1) return false;
|
|
405
|
+
const uint8_t* b = a.s6_addr;
|
|
406
|
+
bool all_zero = true;
|
|
407
|
+
for (int i = 0; i < 16; ++i) { if (b[i]) { all_zero = false; break; } }
|
|
408
|
+
if (all_zero) return false; // :: (unspecified)
|
|
409
|
+
bool loopback = (b[15] == 1);
|
|
410
|
+
for (int i = 0; i < 15; ++i) { if (b[i]) { loopback = false; break; } }
|
|
411
|
+
if (loopback) return false; // ::1
|
|
412
|
+
if ((b[0] & 0xfe) == 0xfc) return false; // fc00::/7 unique local
|
|
413
|
+
if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80) return false; // fe80::/10 link-local
|
|
414
|
+
if (b[0] == 0xff) return false; // ff00::/8 multicast
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
struct in_addr a;
|
|
419
|
+
if (inet_pton(AF_INET, ip.c_str(), &a) != 1) return false;
|
|
420
|
+
uint32_t h = ntohl(a.s_addr);
|
|
421
|
+
uint8_t o1 = static_cast<uint8_t>((h >> 24) & 0xff);
|
|
422
|
+
uint8_t o2 = static_cast<uint8_t>((h >> 16) & 0xff);
|
|
423
|
+
if (o1 == 0) return false; // 0.0.0.0/8
|
|
424
|
+
if (o1 == 127) return false; // loopback
|
|
425
|
+
if (o1 == 10) return false; // 10.0.0.0/8
|
|
426
|
+
if (o1 == 172 && o2 >= 16 && o2 <= 31) return false; // 172.16.0.0/12
|
|
427
|
+
if (o1 == 192 && o2 == 168) return false; // 192.168.0.0/16
|
|
428
|
+
if (o1 == 169 && o2 == 254) return false; // 169.254.0.0/16 link-local
|
|
429
|
+
if (o1 == 100 && o2 >= 64 && o2 <= 127) return false; // 100.64.0.0/10 CGNAT
|
|
430
|
+
if (o1 >= 224) return false; // multicast / reserved
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
|
|
382
434
|
std::vector<std::string> get_local_interface_addresses() {
|
|
383
435
|
LOG_NETUTILS_DEBUG("Getting all local interface addresses (IPv4 and IPv6)");
|
|
384
436
|
|
|
@@ -390,11 +442,140 @@ std::vector<std::string> get_local_interface_addresses() {
|
|
|
390
442
|
auto ipv6_addresses = get_local_interface_addresses_v6();
|
|
391
443
|
addresses.insert(addresses.end(), ipv6_addresses.begin(), ipv6_addresses.end());
|
|
392
444
|
|
|
393
|
-
LOG_NETUTILS_INFO("Found " << addresses.size() << " total local interface addresses ("
|
|
445
|
+
LOG_NETUTILS_INFO("Found " << addresses.size() << " total local interface addresses ("
|
|
394
446
|
<< ipv4_addresses.size() << " IPv4, " << ipv6_addresses.size() << " IPv6)");
|
|
395
|
-
|
|
447
|
+
|
|
396
448
|
return addresses;
|
|
397
449
|
}
|
|
398
450
|
|
|
451
|
+
namespace {
|
|
452
|
+
|
|
453
|
+
// Append unique, non-empty entries preserving order
|
|
454
|
+
void append_unique(std::vector<std::string>& out, const std::string& value) {
|
|
455
|
+
if (value.empty()) return;
|
|
456
|
+
if (std::find(out.begin(), out.end(), value) == out.end()) {
|
|
457
|
+
out.push_back(value);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Best-effort guess: for each local IPv4 assume the gateway is the .1 host of a
|
|
462
|
+
// /24 network. Covers the overwhelming majority of home routers and serves as a
|
|
463
|
+
// fallback when the OS routing table is unavailable.
|
|
464
|
+
void append_gateway_heuristics(std::vector<std::string>& out) {
|
|
465
|
+
for (const auto& ip : get_local_interface_addresses_v4()) {
|
|
466
|
+
if (ip.empty() || ip == "127.0.0.1") continue;
|
|
467
|
+
auto last_dot = ip.find_last_of('.');
|
|
468
|
+
if (last_dot == std::string::npos) continue;
|
|
469
|
+
append_unique(out, ip.substr(0, last_dot) + ".1");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
} // anonymous namespace
|
|
474
|
+
|
|
475
|
+
std::vector<std::string> get_default_gateways() {
|
|
476
|
+
std::vector<std::string> gateways;
|
|
477
|
+
|
|
478
|
+
#ifdef _WIN32
|
|
479
|
+
ULONG out_buf_len = sizeof(IP_ADAPTER_INFO);
|
|
480
|
+
std::vector<uint8_t> buffer(out_buf_len);
|
|
481
|
+
DWORD ret = GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(buffer.data()), &out_buf_len);
|
|
482
|
+
if (ret == ERROR_BUFFER_OVERFLOW) {
|
|
483
|
+
buffer.resize(out_buf_len);
|
|
484
|
+
ret = GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(buffer.data()), &out_buf_len);
|
|
485
|
+
}
|
|
486
|
+
if (ret == NO_ERROR) {
|
|
487
|
+
for (PIP_ADAPTER_INFO adapter = reinterpret_cast<PIP_ADAPTER_INFO>(buffer.data());
|
|
488
|
+
adapter != nullptr; adapter = adapter->Next) {
|
|
489
|
+
for (const IP_ADDR_STRING* gw = &adapter->GatewayList; gw != nullptr; gw = gw->Next) {
|
|
490
|
+
std::string gw_ip(gw->IpAddress.String);
|
|
491
|
+
if (gw_ip != "0.0.0.0") {
|
|
492
|
+
append_unique(gateways, gw_ip);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
LOG_NETUTILS_DEBUG("GetAdaptersInfo failed with error: " << ret);
|
|
498
|
+
}
|
|
499
|
+
#elif defined(__linux__)
|
|
500
|
+
// /proc/net/route columns: Iface Destination Gateway Flags ... (hex, little-endian)
|
|
501
|
+
if (FILE* f = std::fopen("/proc/net/route", "r")) {
|
|
502
|
+
char line[256];
|
|
503
|
+
// Skip header line
|
|
504
|
+
if (std::fgets(line, sizeof(line), f)) {
|
|
505
|
+
char iface[64];
|
|
506
|
+
unsigned long dest = 0, gw = 0;
|
|
507
|
+
while (std::fgets(line, sizeof(line), f)) {
|
|
508
|
+
if (std::sscanf(line, "%63s %lx %lx", iface, &dest, &gw) == 3) {
|
|
509
|
+
if (dest == 0 && gw != 0) {
|
|
510
|
+
struct in_addr addr;
|
|
511
|
+
addr.s_addr = static_cast<in_addr_t>(gw);
|
|
512
|
+
char ip_str[INET_ADDRSTRLEN];
|
|
513
|
+
if (inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str))) {
|
|
514
|
+
append_unique(gateways, ip_str);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
std::fclose(f);
|
|
521
|
+
}
|
|
522
|
+
#elif defined(RATS_HAVE_BSD_ROUTES)
|
|
523
|
+
// Dump the IPv4 routing table and pick the gateway of the default route(s).
|
|
524
|
+
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0 };
|
|
525
|
+
size_t needed = 0;
|
|
526
|
+
if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) == 0 && needed > 0) {
|
|
527
|
+
std::vector<char> buf(needed);
|
|
528
|
+
if (sysctl(mib, 6, buf.data(), &needed, nullptr, 0) == 0) {
|
|
529
|
+
// sockaddrs in a routing message are padded to a 4-byte boundary.
|
|
530
|
+
auto sa_roundup = [](socklen_t len) -> size_t {
|
|
531
|
+
return len ? (1 + ((static_cast<size_t>(len) - 1) | (sizeof(uint32_t) - 1)))
|
|
532
|
+
: sizeof(uint32_t);
|
|
533
|
+
};
|
|
534
|
+
char* lim = buf.data() + needed;
|
|
535
|
+
for (char* next = buf.data(); next + sizeof(struct rt_msghdr) <= lim; ) {
|
|
536
|
+
auto* rtm = reinterpret_cast<struct rt_msghdr*>(next);
|
|
537
|
+
if (rtm->rtm_msglen == 0) break;
|
|
538
|
+
char* msg_end = next + rtm->rtm_msglen;
|
|
539
|
+
next = msg_end;
|
|
540
|
+
|
|
541
|
+
if (!(rtm->rtm_flags & RTF_GATEWAY)) continue;
|
|
542
|
+
if (!(rtm->rtm_addrs & RTA_DST) || !(rtm->rtm_addrs & RTA_GATEWAY)) continue;
|
|
543
|
+
|
|
544
|
+
// Address list follows the header, ordered by the RTA_* bit flags.
|
|
545
|
+
char* sa_ptr = reinterpret_cast<char*>(rtm + 1);
|
|
546
|
+
struct sockaddr* dst = nullptr;
|
|
547
|
+
struct sockaddr* gw = nullptr;
|
|
548
|
+
for (int bit = 1; bit && sa_ptr < msg_end; bit <<= 1) {
|
|
549
|
+
if (!(rtm->rtm_addrs & bit)) continue;
|
|
550
|
+
auto* sa = reinterpret_cast<struct sockaddr*>(sa_ptr);
|
|
551
|
+
if (bit == RTA_DST) dst = sa;
|
|
552
|
+
else if (bit == RTA_GATEWAY) gw = sa;
|
|
553
|
+
sa_ptr += sa_roundup(sa->sa_len);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (!dst || !gw) continue;
|
|
557
|
+
if (dst->sa_family != AF_INET || gw->sa_family != AF_INET) continue;
|
|
558
|
+
// Default route: destination 0.0.0.0
|
|
559
|
+
if (reinterpret_cast<struct sockaddr_in*>(dst)->sin_addr.s_addr != 0) continue;
|
|
560
|
+
|
|
561
|
+
char ip_str[INET_ADDRSTRLEN];
|
|
562
|
+
auto* gw4 = reinterpret_cast<struct sockaddr_in*>(gw);
|
|
563
|
+
if (inet_ntop(AF_INET, &gw4->sin_addr, ip_str, sizeof(ip_str))) {
|
|
564
|
+
append_unique(gateways, ip_str);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
LOG_NETUTILS_DEBUG("PF_ROUTE sysctl for default gateway failed");
|
|
570
|
+
}
|
|
571
|
+
#endif
|
|
572
|
+
|
|
573
|
+
// Always add heuristics as a fallback so callers have something to try
|
|
574
|
+
append_gateway_heuristics(gateways);
|
|
575
|
+
|
|
576
|
+
LOG_NETUTILS_INFO("Detected " << gateways.size() << " default gateway candidate(s)");
|
|
577
|
+
return gateways;
|
|
578
|
+
}
|
|
579
|
+
|
|
399
580
|
} // namespace network_utils
|
|
400
581
|
} // namespace librats
|
|
@@ -41,11 +41,38 @@ bool is_valid_ipv6(const std::string& ip_str);
|
|
|
41
41
|
*/
|
|
42
42
|
bool is_hostname(const std::string& str);
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Check whether an IP address is publicly routable (not a private/reserved range).
|
|
46
|
+
*
|
|
47
|
+
* Returns false for RFC1918 (10/8, 172.16/12, 192.168/16), CGNAT (100.64/10),
|
|
48
|
+
* loopback, link-local (169.254/16, fe80::/10), unspecified, multicast/reserved,
|
|
49
|
+
* and IPv6 unique-local (fc00::/7). A non-IP / unparseable string yields false.
|
|
50
|
+
*
|
|
51
|
+
* Used both by the DHT (BEP 42 external-IP voting) and by automatic port
|
|
52
|
+
* forwarding to detect a double-NAT gateway whose reported "external" IP is itself
|
|
53
|
+
* private and therefore not a usable public endpoint.
|
|
54
|
+
*/
|
|
55
|
+
bool is_public_ip(const std::string& ip);
|
|
56
|
+
|
|
44
57
|
/**
|
|
45
58
|
* Get all local network interface addresses (IPv4 and IPv6)
|
|
46
59
|
* @return Vector of local IP addresses from all network interfaces
|
|
47
60
|
*/
|
|
48
61
|
std::vector<std::string> get_local_interface_addresses();
|
|
49
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Get the default IPv4 gateway address(es) of the host.
|
|
65
|
+
*
|
|
66
|
+
* Used for NAT port forwarding (NAT-PMP talks to the gateway directly, and UPnP
|
|
67
|
+
* can use it to restrict discovery to the local router). The OS routing table is
|
|
68
|
+
* consulted where available (Windows iphlpapi, Linux /proc/net/route). When the
|
|
69
|
+
* platform routing table cannot be read, a best-effort heuristic derived from the
|
|
70
|
+
* local IPv4 addresses (network .1) is appended so callers still have a candidate
|
|
71
|
+
* to try.
|
|
72
|
+
*
|
|
73
|
+
* @return Vector of gateway IPv4 addresses, most specific first. May be empty.
|
|
74
|
+
*/
|
|
75
|
+
std::vector<std::string> get_default_gateways();
|
|
76
|
+
|
|
50
77
|
} // namespace network_utils
|
|
51
78
|
} // namespace librats
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file port_mapping.h
|
|
5
|
+
* @brief Shared types for automatic NAT port forwarding (UPnP IGD + NAT-PMP)
|
|
6
|
+
*
|
|
7
|
+
* Both the UPnP (@ref UpnpClient) and NAT-PMP (@ref NatPmpClient) backends ask a
|
|
8
|
+
* home router to forward an external (WAN) port to a local (LAN) port so that
|
|
9
|
+
* inbound peer connections can reach a host behind NAT. They share the small set
|
|
10
|
+
* of vocabulary types defined here: the transport protocol of the mapping, which
|
|
11
|
+
* backend produced a result, and the result/callback shape RatsClient consumes.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#include <cstdint>
|
|
15
|
+
#include <string>
|
|
16
|
+
#include <functional>
|
|
17
|
+
|
|
18
|
+
namespace librats {
|
|
19
|
+
|
|
20
|
+
/// Transport protocol of a port mapping.
|
|
21
|
+
enum class PortMapProtocol : uint8_t {
|
|
22
|
+
TCP,
|
|
23
|
+
UDP
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/// Which NAT traversal backend produced a result.
|
|
27
|
+
enum class PortMapTransport : uint8_t {
|
|
28
|
+
UPnP,
|
|
29
|
+
NatPMP
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/// Human readable protocol name ("TCP"/"UDP").
|
|
33
|
+
inline const char* to_string(PortMapProtocol p) {
|
|
34
|
+
return p == PortMapProtocol::TCP ? "TCP" : "UDP";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Human readable transport name ("UPnP"/"NAT-PMP").
|
|
38
|
+
inline const char* to_string(PortMapTransport t) {
|
|
39
|
+
return t == PortMapTransport::UPnP ? "UPnP" : "NAT-PMP";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Result of a port mapping attempt.
|
|
44
|
+
*
|
|
45
|
+
* On success @ref external_port holds the public port the router assigned (which
|
|
46
|
+
* may differ from the requested one) and, when the backend can report it,
|
|
47
|
+
* @ref external_ip holds the discovered public IP address.
|
|
48
|
+
*/
|
|
49
|
+
struct PortMapResult {
|
|
50
|
+
PortMapTransport transport; ///< Backend that produced this result
|
|
51
|
+
PortMapProtocol protocol; ///< Protocol of the mapping
|
|
52
|
+
bool success = false; ///< Whether the mapping is currently active
|
|
53
|
+
uint16_t internal_port = 0; ///< Local (LAN) port that was mapped
|
|
54
|
+
uint16_t external_port = 0; ///< Public (WAN) port assigned by the router
|
|
55
|
+
std::string external_ip; ///< Discovered public IP (may be empty)
|
|
56
|
+
std::string error; ///< Human readable error when !success
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Callback invoked whenever a mapping is established, refreshed, removed or fails.
|
|
61
|
+
* Always called from the backend's own worker thread.
|
|
62
|
+
*/
|
|
63
|
+
using PortMapCallback = std::function<void(const PortMapResult&)>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Configuration for RatsClient's automatic port forwarding.
|
|
67
|
+
*
|
|
68
|
+
* Both backends run in parallel by default; whichever the router supports
|
|
69
|
+
* succeeds. Disabling one (or all) of them is a matter of flipping a flag.
|
|
70
|
+
*/
|
|
71
|
+
struct PortMappingConfig {
|
|
72
|
+
bool enabled = true; ///< Master switch for automatic port forwarding
|
|
73
|
+
bool enable_upnp = true; ///< Use the UPnP IGD backend
|
|
74
|
+
bool enable_natpmp = true; ///< Use the NAT-PMP backend
|
|
75
|
+
uint32_t lease_duration_seconds = 3600; ///< Requested lease duration
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
} // namespace librats
|
|
@@ -844,18 +844,29 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data,
|
|
|
844
844
|
}
|
|
845
845
|
|
|
846
846
|
std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer& sender_peer,
|
|
847
|
-
int timeout_ms) {
|
|
848
|
-
// Handle timeout using select
|
|
849
|
-
|
|
847
|
+
int timeout_ms, socket_t interrupt_fd) {
|
|
848
|
+
// Handle timeout (and optional interrupt socket) using select. When no interrupt
|
|
849
|
+
// fd is supplied this path is identical to the plain timeout behavior.
|
|
850
|
+
const bool have_interrupt = is_valid_socket(interrupt_fd);
|
|
851
|
+
if (timeout_ms >= 0 || have_interrupt) {
|
|
850
852
|
fd_set read_fds;
|
|
851
853
|
FD_ZERO(&read_fds);
|
|
852
854
|
FD_SET(socket, &read_fds);
|
|
855
|
+
socket_t maxfd = socket;
|
|
856
|
+
if (have_interrupt) {
|
|
857
|
+
FD_SET(interrupt_fd, &read_fds);
|
|
858
|
+
if (interrupt_fd > maxfd) maxfd = interrupt_fd;
|
|
859
|
+
}
|
|
853
860
|
|
|
854
861
|
struct timeval timeout;
|
|
855
|
-
|
|
856
|
-
|
|
862
|
+
struct timeval* ptimeout = nullptr; // timeout_ms < 0 => block until readable
|
|
863
|
+
if (timeout_ms >= 0) {
|
|
864
|
+
timeout.tv_sec = timeout_ms / 1000;
|
|
865
|
+
timeout.tv_usec = (timeout_ms % 1000) * 1000;
|
|
866
|
+
ptimeout = &timeout;
|
|
867
|
+
}
|
|
857
868
|
|
|
858
|
-
int result = select(
|
|
869
|
+
int result = select(static_cast<int>(maxfd) + 1, &read_fds, nullptr, nullptr, ptimeout);
|
|
859
870
|
if (result == 0) {
|
|
860
871
|
LOG_SOCKET_DEBUG("UDP receive timeout (" << timeout_ms << "ms)");
|
|
861
872
|
return {};
|
|
@@ -863,6 +874,15 @@ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer&
|
|
|
863
874
|
LOG_SOCKET_ERROR("Select error while waiting for UDP data");
|
|
864
875
|
return {};
|
|
865
876
|
}
|
|
877
|
+
// Woken by the interrupt socket (e.g. stop requested): leave the data socket
|
|
878
|
+
// untouched and report no data so the caller can re-check its stop flag.
|
|
879
|
+
if (have_interrupt && FD_ISSET(interrupt_fd, &read_fds)) {
|
|
880
|
+
return {};
|
|
881
|
+
}
|
|
882
|
+
// Guard against calling recvfrom on a data socket that isn't actually ready.
|
|
883
|
+
if (!FD_ISSET(socket, &read_fds)) {
|
|
884
|
+
return {};
|
|
885
|
+
}
|
|
866
886
|
}
|
|
867
887
|
|
|
868
888
|
std::vector<uint8_t> buffer(buffer_size);
|
package/native-src/src/socket.h
CHANGED
|
@@ -170,10 +170,14 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data, const std::
|
|
|
170
170
|
* @param buffer_size Maximum number of bytes to receive
|
|
171
171
|
* @param sender_peer Output parameter for the sender's peer info
|
|
172
172
|
* @param timeout_ms Timeout in milliseconds (-1 for blocking, 0 for non-blocking, >0 for timeout)
|
|
173
|
-
* @
|
|
173
|
+
* @param interrupt_fd Optional second socket to watch; when it becomes readable the
|
|
174
|
+
* call returns immediately with an empty vector (used to wake a
|
|
175
|
+
* blocking receive on shutdown). INVALID_SOCKET_VALUE disables it.
|
|
176
|
+
* @return Received data, empty vector on timeout, error or interrupt
|
|
174
177
|
*/
|
|
175
178
|
std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer& sender_peer,
|
|
176
|
-
int timeout_ms = -1
|
|
179
|
+
int timeout_ms = -1,
|
|
180
|
+
socket_t interrupt_fd = INVALID_SOCKET_VALUE);
|
|
177
181
|
|
|
178
182
|
// Common Socket Functions
|
|
179
183
|
/**
|