librats 0.9.1 → 1.0.0
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 +14 -0
- package/native-src/src/dht.cpp +3 -32
- package/native-src/src/dht.h +9 -1
- package/native-src/src/librats.cpp +42 -12
- package/native-src/src/librats.h +146 -2
- 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 +419 -0
- package/native-src/src/natpmp.cpp +416 -0
- package/native-src/src/natpmp.h +140 -0
- package/native-src/src/network_monitor.cpp +367 -0
- package/native-src/src/network_monitor.h +104 -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
|
@@ -132,6 +132,8 @@ set(LIBRARY_SOURCES
|
|
|
132
132
|
src/socket.h
|
|
133
133
|
src/network_utils.cpp
|
|
134
134
|
src/network_utils.h
|
|
135
|
+
src/network_monitor.cpp
|
|
136
|
+
src/network_monitor.h
|
|
135
137
|
src/dht.cpp
|
|
136
138
|
src/dht.h
|
|
137
139
|
src/bencode.cpp
|
|
@@ -212,6 +214,14 @@ set(LIBRARY_SOURCES
|
|
|
212
214
|
src/ice.cpp
|
|
213
215
|
src/ice.h
|
|
214
216
|
src/librats_ice.cpp
|
|
217
|
+
|
|
218
|
+
# Automatic port forwarding (UPnP IGD / NAT-PMP)
|
|
219
|
+
src/port_mapping.h
|
|
220
|
+
src/upnp.cpp
|
|
221
|
+
src/upnp.h
|
|
222
|
+
src/natpmp.cpp
|
|
223
|
+
src/natpmp.h
|
|
224
|
+
src/librats_portmap.cpp
|
|
215
225
|
)
|
|
216
226
|
|
|
217
227
|
# Add BitTorrent sources if RATS_SEARCH_FEATURES is enabled
|
|
@@ -458,6 +468,10 @@ if(RATS_BUILD_TESTS)
|
|
|
458
468
|
tests/test_stun.cpp
|
|
459
469
|
tests/test_turn.cpp
|
|
460
470
|
tests/test_ice.cpp
|
|
471
|
+
# Automatic port forwarding tests (UPnP/NAT-PMP)
|
|
472
|
+
tests/test_portmap.cpp
|
|
473
|
+
# Network change detection tests
|
|
474
|
+
tests/test_network_monitor.cpp
|
|
461
475
|
# I/O poller abstraction (epoll/kqueue/IOCP)
|
|
462
476
|
tests/test_io_poller.cpp
|
|
463
477
|
# Buffer utilities for file transfer and bittorrent
|
package/native-src/src/dht.cpp
CHANGED
|
@@ -1258,38 +1258,9 @@ bool bep42_prefix(const std::string& ip, uint8_t seed, uint8_t prefix[3]) {
|
|
|
1258
1258
|
} // namespace
|
|
1259
1259
|
|
|
1260
1260
|
bool DhtClient::is_public_address(const std::string& ip) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
struct in6_addr a;
|
|
1265
|
-
if (inet_pton(AF_INET6, ip.c_str(), &a) != 1) return false;
|
|
1266
|
-
const uint8_t* b = a.s6_addr;
|
|
1267
|
-
bool all_zero = true;
|
|
1268
|
-
for (int i = 0; i < 16; ++i) { if (b[i]) { all_zero = false; break; } }
|
|
1269
|
-
if (all_zero) return false; // :: (unspecified)
|
|
1270
|
-
bool loopback = (b[15] == 1);
|
|
1271
|
-
for (int i = 0; i < 15; ++i) { if (b[i]) { loopback = false; break; } }
|
|
1272
|
-
if (loopback) return false; // ::1
|
|
1273
|
-
if ((b[0] & 0xfe) == 0xfc) return false; // fc00::/7 unique local
|
|
1274
|
-
if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80) return false; // fe80::/10 link-local
|
|
1275
|
-
if (b[0] == 0xff) return false; // ff00::/8 multicast
|
|
1276
|
-
return true;
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
struct in_addr a;
|
|
1280
|
-
if (inet_pton(AF_INET, ip.c_str(), &a) != 1) return false;
|
|
1281
|
-
uint32_t h = ntohl(a.s_addr);
|
|
1282
|
-
uint8_t o1 = static_cast<uint8_t>((h >> 24) & 0xff);
|
|
1283
|
-
uint8_t o2 = static_cast<uint8_t>((h >> 16) & 0xff);
|
|
1284
|
-
if (o1 == 0) return false; // 0.0.0.0/8
|
|
1285
|
-
if (o1 == 127) return false; // loopback
|
|
1286
|
-
if (o1 == 10) return false; // 10.0.0.0/8
|
|
1287
|
-
if (o1 == 172 && o2 >= 16 && o2 <= 31) return false; // 172.16.0.0/12
|
|
1288
|
-
if (o1 == 192 && o2 == 168) return false; // 192.168.0.0/16
|
|
1289
|
-
if (o1 == 169 && o2 == 254) return false; // 169.254.0.0/16 link-local
|
|
1290
|
-
if (o1 == 100 && o2 >= 64 && o2 <= 127) return false; // 100.64.0.0/10 CGNAT
|
|
1291
|
-
if (o1 >= 224) return false; // multicast / reserved
|
|
1292
|
-
return true;
|
|
1261
|
+
// Shared with automatic port forwarding; the canonical classifier lives in
|
|
1262
|
+
// network_utils so both subsystems agree on what "publicly routable" means.
|
|
1263
|
+
return network_utils::is_public_ip(ip);
|
|
1293
1264
|
}
|
|
1294
1265
|
|
|
1295
1266
|
bool DhtClient::generate_node_id_from_ip(const std::string& ip, NodeId& out, std::mt19937& gen) {
|
package/native-src/src/dht.h
CHANGED
|
@@ -298,7 +298,15 @@ public:
|
|
|
298
298
|
* @return true if running, false otherwise
|
|
299
299
|
*/
|
|
300
300
|
bool is_running() const { return running_; }
|
|
301
|
-
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Actual bound UDP port. May differ from the requested port if it was taken and
|
|
304
|
+
* the client fell back to an ephemeral port (see start()). Use this — not the
|
|
305
|
+
* requested port — when forwarding the DHT port through a router.
|
|
306
|
+
* @return bound port, or 0 if not started
|
|
307
|
+
*/
|
|
308
|
+
uint16_t get_port() const { return static_cast<uint16_t>(port_); }
|
|
309
|
+
|
|
302
310
|
#ifdef RATS_SEARCH_FEATURES
|
|
303
311
|
// ============================================================================
|
|
304
312
|
// SPIDER MODE - Aggressive node discovery and announce collection
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "librats.h"
|
|
2
2
|
#include "os.h"
|
|
3
3
|
#include "network_utils.h"
|
|
4
|
+
#include "network_monitor.h" // complete type for unique_ptr<NetworkMonitor> member
|
|
4
5
|
#include "version.h"
|
|
5
6
|
#include <algorithm>
|
|
6
7
|
#include <array>
|
|
@@ -166,7 +167,15 @@ bool RatsClient::start() {
|
|
|
166
167
|
if (gossipsub_ && !gossipsub_->start()) {
|
|
167
168
|
LOG_CLIENT_WARN("Failed to start GossipSub - continuing without it");
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
+
|
|
171
|
+
// Start automatic port forwarding (UPnP/NAT-PMP) for the bound listen port.
|
|
172
|
+
// No-op when disabled; runs discovery/mapping on its own background threads.
|
|
173
|
+
start_port_mapping();
|
|
174
|
+
|
|
175
|
+
// React to host network changes (IP/interface/route): renew port mappings,
|
|
176
|
+
// re-discover the public address and re-announce. No-op when disabled.
|
|
177
|
+
start_network_monitor();
|
|
178
|
+
|
|
170
179
|
LOG_CLIENT_INFO("RatsClient started successfully on port " << listen_port_);
|
|
171
180
|
|
|
172
181
|
// Attempt to reconnect to saved peers
|
|
@@ -197,7 +206,16 @@ void RatsClient::stop() {
|
|
|
197
206
|
}
|
|
198
207
|
|
|
199
208
|
LOG_CLIENT_INFO("Stopping RatsClient");
|
|
200
|
-
|
|
209
|
+
|
|
210
|
+
// Stop network-change detection FIRST and join its recovery worker: that
|
|
211
|
+
// worker touches the port-mapping backends and the DHT clients, both torn
|
|
212
|
+
// down below, so it must not be running past this point.
|
|
213
|
+
stop_network_monitor();
|
|
214
|
+
|
|
215
|
+
// Remove port mappings and stop UPnP/NAT-PMP backends before tearing down
|
|
216
|
+
// sockets (best-effort cleanup so we don't leave stale router mappings).
|
|
217
|
+
stop_port_mapping();
|
|
218
|
+
|
|
201
219
|
// Stop GossipSub (can broadcast stop message)
|
|
202
220
|
if (gossipsub_) {
|
|
203
221
|
gossipsub_->stop();
|
|
@@ -1379,22 +1397,34 @@ static constexpr std::array<std::string_view,5> localhost_addrs{"127.0.0.1", "::
|
|
|
1379
1397
|
|
|
1380
1398
|
// Local interface address blocking methods
|
|
1381
1399
|
void RatsClient::initialize_local_addresses() {
|
|
1382
|
-
LOG_CLIENT_INFO("Initializing local interface addresses for connection blocking");
|
|
1383
|
-
|
|
1384
1400
|
std::lock_guard<std::mutex> lock(local_addresses_mutex_);
|
|
1385
|
-
|
|
1386
|
-
//
|
|
1401
|
+
|
|
1402
|
+
// (Re)enumerate the host's interface addresses. This is also called on every
|
|
1403
|
+
// network change, so it must be a diff, not a blind insert: drop auto-detected
|
|
1404
|
+
// addresses that have disappeared (a removed IP must stop being treated as
|
|
1405
|
+
// "ourselves"), while preserving localhost entries and any externally-
|
|
1406
|
+
// discovered / manually-ignored addresses (STUN reflexive, mapped external IP,
|
|
1407
|
+
// user add_ignored_address()), which live in the same set.
|
|
1387
1408
|
auto addrs = network_utils::get_local_interface_addresses();
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1409
|
+
std::unordered_set<std::string> new_auto(addrs.begin(), addrs.end());
|
|
1410
|
+
|
|
1411
|
+
for (const auto& old : auto_interface_addresses_) {
|
|
1412
|
+
if (new_auto.find(old) == new_auto.end()) {
|
|
1413
|
+
local_interface_addresses_.erase(old);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
for (const auto& addr : addrs) {
|
|
1417
|
+
local_interface_addresses_.insert(addr);
|
|
1418
|
+
}
|
|
1391
1419
|
for (const auto& addr : localhost_addrs) {
|
|
1392
1420
|
local_interface_addresses_.emplace(std::string(addr));
|
|
1393
1421
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1422
|
+
auto_interface_addresses_ = std::move(new_auto);
|
|
1423
|
+
|
|
1424
|
+
LOG_CLIENT_INFO("Local interface addresses: " << local_interface_addresses_.size()
|
|
1425
|
+
<< " blocked (" << addrs.size() << " from interfaces)");
|
|
1396
1426
|
for (const auto& addr : local_interface_addresses_) {
|
|
1397
|
-
|
|
1427
|
+
LOG_CLIENT_DEBUG(" - " << addr);
|
|
1398
1428
|
}
|
|
1399
1429
|
}
|
|
1400
1430
|
|
package/native-src/src/librats.h
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
#include "file_transfer.h" // File transfer functionality
|
|
10
10
|
#include "noise.h" // Noise Protocol encryption
|
|
11
11
|
#include "ice.h" // ICE-lite NAT traversal
|
|
12
|
+
#include "upnp.h" // UPnP IGD automatic port forwarding
|
|
13
|
+
#include "natpmp.h" // NAT-PMP automatic port forwarding
|
|
12
14
|
#include "io_poller.h" // Platform-optimal I/O multiplexing
|
|
13
15
|
#include "receive_buffer.h" // Efficient receive buffer for async I/O
|
|
14
16
|
#include "chained_send_buffer.h" // Zero-copy chained send buffer
|
|
@@ -36,6 +38,11 @@
|
|
|
36
38
|
|
|
37
39
|
namespace librats {
|
|
38
40
|
|
|
41
|
+
// Detects host network configuration changes (defined in network_monitor.h).
|
|
42
|
+
// Forward-declared here; only used via unique_ptr, so RatsClient's destructor
|
|
43
|
+
// (defined in librats.cpp, which includes network_monitor.h) sees the full type.
|
|
44
|
+
class NetworkMonitor;
|
|
45
|
+
|
|
39
46
|
/**
|
|
40
47
|
* PeerIOContext - Per-peer async I/O buffers and framing state
|
|
41
48
|
*
|
|
@@ -280,6 +287,10 @@ public:
|
|
|
280
287
|
using DisconnectCallback = std::function<void(socket_t, const std::string&)>;
|
|
281
288
|
using MessageCallback = std::function<void(const std::string&, const nlohmann::json&)>;
|
|
282
289
|
using SendCallback = std::function<void(bool, const std::string&)>;
|
|
290
|
+
// Fired when the host's network configuration changes (interface up/down, IP
|
|
291
|
+
// added/removed, default route change). The argument is the new full list of
|
|
292
|
+
// local interface addresses. See on_network_changed().
|
|
293
|
+
using NetworkChangeCallback = std::function<void(const std::vector<std::string>& local_addresses)>;
|
|
283
294
|
|
|
284
295
|
// =========================================================================
|
|
285
296
|
// Constructor and Destructor
|
|
@@ -1432,6 +1443,79 @@ public:
|
|
|
1432
1443
|
*/
|
|
1433
1444
|
void restart_ice();
|
|
1434
1445
|
|
|
1446
|
+
// =========================================================================
|
|
1447
|
+
// Automatic Port Forwarding API (UPnP IGD + NAT-PMP)
|
|
1448
|
+
// =========================================================================
|
|
1449
|
+
//
|
|
1450
|
+
// When enabled (the default), RatsClient asks the home router to forward the
|
|
1451
|
+
// TCP listen port on startup, using UPnP and NAT-PMP in parallel (whichever
|
|
1452
|
+
// the router supports wins). Mappings are refreshed automatically and removed
|
|
1453
|
+
// on stop(). This lets peers behind a NAT accept inbound connections without
|
|
1454
|
+
// manual router configuration.
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Enable or disable automatic port forwarding. If toggled while running, the
|
|
1458
|
+
* port mapping backends are started or stopped immediately. The setting is
|
|
1459
|
+
* persisted to config.json.
|
|
1460
|
+
*/
|
|
1461
|
+
void set_port_mapping_enabled(bool enabled);
|
|
1462
|
+
|
|
1463
|
+
/// Whether automatic port forwarding is currently enabled.
|
|
1464
|
+
bool is_port_mapping_enabled() const;
|
|
1465
|
+
|
|
1466
|
+
/// Replace the full port mapping configuration (takes effect on next start).
|
|
1467
|
+
void set_port_mapping_config(const PortMappingConfig& config);
|
|
1468
|
+
|
|
1469
|
+
/// Get the current port mapping configuration.
|
|
1470
|
+
PortMappingConfig get_port_mapping_config() const;
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Request an additional port mapping beyond the automatic listen-port mapping
|
|
1474
|
+
* (e.g. a DHT UDP port). Has effect only while port mapping is enabled.
|
|
1475
|
+
*/
|
|
1476
|
+
void add_port_mapping(PortMapProtocol protocol, uint16_t port);
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
* Get the public (external) address discovered by the port mapping backends.
|
|
1480
|
+
* @return {external_ip, external_port} if a mapping is active, otherwise nullopt
|
|
1481
|
+
*/
|
|
1482
|
+
std::optional<std::pair<std::string, uint16_t>> get_mapped_public_address() const;
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Register a callback fired whenever a port mapping is established, refreshed,
|
|
1486
|
+
* removed or fails (invoked from a backend worker thread).
|
|
1487
|
+
*/
|
|
1488
|
+
void on_port_mapping(PortMapCallback callback);
|
|
1489
|
+
|
|
1490
|
+
// =========================================================================
|
|
1491
|
+
// Network change detection
|
|
1492
|
+
// =========================================================================
|
|
1493
|
+
//
|
|
1494
|
+
// A long-lived node must notice when the host's connectivity changes (a new
|
|
1495
|
+
// interface, an IP added/removed, the default route flipping between Wi-Fi
|
|
1496
|
+
// and cellular, dock/undock, VPN up/down, wake-from-sleep) and recover:
|
|
1497
|
+
// renew router port mappings, re-discover its public address via STUN, and
|
|
1498
|
+
// re-announce to the DHT. Otherwise it keeps advertising a stale endpoint
|
|
1499
|
+
// until the next periodic refresh. Enabled by default. Implemented in
|
|
1500
|
+
// librats_portmap.cpp on top of the platform-specific NetworkMonitor.
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* Enable or disable automatic reaction to host network changes. Enabled by
|
|
1504
|
+
* default. Can be called before or after start(): toggling while running
|
|
1505
|
+
* starts/stops the monitor immediately. Not persisted.
|
|
1506
|
+
*/
|
|
1507
|
+
void set_network_change_detection_enabled(bool enabled);
|
|
1508
|
+
bool is_network_change_detection_enabled() const;
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* Register a callback fired (debounced) whenever the set of local interface
|
|
1512
|
+
* addresses changes. The argument is the new full list of local addresses.
|
|
1513
|
+
* Invoked from the monitor's worker thread, so keep the handler quick. This
|
|
1514
|
+
* is in addition to — not a replacement for — the built-in recovery
|
|
1515
|
+
* (port re-mapping, STUN re-discovery, DHT re-announce).
|
|
1516
|
+
*/
|
|
1517
|
+
void on_network_changed(NetworkChangeCallback callback);
|
|
1518
|
+
|
|
1435
1519
|
#ifdef RATS_STORAGE
|
|
1436
1520
|
// =========================================================================
|
|
1437
1521
|
// Distributed Storage API (requires RATS_STORAGE)
|
|
@@ -1918,6 +2002,11 @@ private:
|
|
|
1918
2002
|
// 6. io_mutex_ (I/O poller and send buffer access)
|
|
1919
2003
|
// 7. message_handlers_mutex_ (Message handler registration)
|
|
1920
2004
|
// 8. reconnect_mutex_ (Reconnection queue management)
|
|
2005
|
+
// 9. port_mapping_mutex_ (UPnP/NAT-PMP backends and mapped address)
|
|
2006
|
+
// 10. network_monitor_mutex_ / network_recovery_mutex_ (network-change state)
|
|
2007
|
+
//
|
|
2008
|
+
// (9) and (10) are leaf locks: they are never held while acquiring any lock
|
|
2009
|
+
// above, so they impose no additional ordering constraints.
|
|
1921
2010
|
// =========================================================================
|
|
1922
2011
|
|
|
1923
2012
|
// [1] Configuration persistence (protected by config_mutex_)
|
|
@@ -1942,7 +2031,12 @@ private:
|
|
|
1942
2031
|
// [4] Local interface address blocking (protected by local_addresses_mutex_)
|
|
1943
2032
|
mutable std::mutex local_addresses_mutex_; // [4] Protects local interface addresses
|
|
1944
2033
|
std::unordered_set<std::string> local_interface_addresses_;
|
|
1945
|
-
|
|
2034
|
+
// Subset of local_interface_addresses_ that came from interface enumeration.
|
|
2035
|
+
// Tracked separately so a network-change refresh can drop only stale auto
|
|
2036
|
+
// entries without evicting localhost or externally-discovered addresses
|
|
2037
|
+
// (STUN reflexive, mapped external IP, user add_ignored_address()).
|
|
2038
|
+
std::unordered_set<std::string> auto_interface_addresses_;
|
|
2039
|
+
|
|
1946
2040
|
// [5] Organized peer management using RatsPeer struct (protected by peers_mutex_)
|
|
1947
2041
|
mutable std::mutex peers_mutex_; // [5] Protects peer data (most frequently locked)
|
|
1948
2042
|
std::unordered_map<std::string, RatsPeer> peers_; // keyed by peer_id
|
|
@@ -1981,7 +2075,57 @@ private:
|
|
|
1981
2075
|
|
|
1982
2076
|
// ICE manager for NAT traversal
|
|
1983
2077
|
std::unique_ptr<IceManager> ice_manager_;
|
|
1984
|
-
|
|
2078
|
+
|
|
2079
|
+
// Automatic port forwarding (UPnP IGD + NAT-PMP). Implemented in librats_portmap.cpp.
|
|
2080
|
+
mutable std::mutex port_mapping_mutex_; // guards the fields below
|
|
2081
|
+
PortMappingConfig port_mapping_config_;
|
|
2082
|
+
std::unique_ptr<UpnpClient> upnp_client_;
|
|
2083
|
+
std::unique_ptr<NatPmpClient> natpmp_client_;
|
|
2084
|
+
PortMapCallback port_mapping_callback_;
|
|
2085
|
+
// Public address discovered by the backends. The external port is tracked per
|
|
2086
|
+
// protocol (like libtorrent's per-listen-socket tcp/udp port mappings): the TCP
|
|
2087
|
+
// mapping forwards the peer listen port, the UDP mapping forwards the DHT port.
|
|
2088
|
+
// A single field would let the two backend callbacks clobber each other.
|
|
2089
|
+
std::string mapped_external_ip_;
|
|
2090
|
+
uint16_t mapped_external_tcp_port_ = 0; // public port for the TCP peer-listen port
|
|
2091
|
+
uint16_t mapped_external_udp_port_ = 0; // public port for the UDP DHT port
|
|
2092
|
+
// Set once we warn that the gateway's reported external IP is itself private
|
|
2093
|
+
// (double-NAT), so the warning isn't repeated on every lease refresh.
|
|
2094
|
+
bool double_nat_warning_logged_ = false;
|
|
2095
|
+
|
|
2096
|
+
// Start/stop the port mapping backends (no-ops if disabled). Called from
|
|
2097
|
+
// start()/stop(); safe to call repeatedly.
|
|
2098
|
+
void start_port_mapping();
|
|
2099
|
+
void stop_port_mapping();
|
|
2100
|
+
void handle_port_mapping_result(const PortMapResult& result);
|
|
2101
|
+
// Public TCP port to advertise to peers/DHT: the mapped external port once a
|
|
2102
|
+
// TCP mapping is established, otherwise the local listen port.
|
|
2103
|
+
uint16_t get_advertised_port() const;
|
|
2104
|
+
|
|
2105
|
+
// Network change detection (implemented in librats_portmap.cpp). Monitor runs
|
|
2106
|
+
// a platform watcher; on a real address-set change it refreshes the self-
|
|
2107
|
+
// address set inline and wakes the recovery worker, which re-maps ports,
|
|
2108
|
+
// re-discovers the public IP and re-announces. These mutexes are last in the
|
|
2109
|
+
// lock order (after [8]) and are never held while calling into other
|
|
2110
|
+
// subsystems, so they introduce no new ordering constraints.
|
|
2111
|
+
std::unique_ptr<NetworkMonitor> network_monitor_;
|
|
2112
|
+
bool network_change_detection_enabled_ = true; // start the monitor on start()
|
|
2113
|
+
mutable std::mutex network_monitor_mutex_; // guards the user callback below
|
|
2114
|
+
NetworkChangeCallback network_change_callback_;
|
|
2115
|
+
// Dedicated recovery worker: serialises the slow recovery work (STUN can
|
|
2116
|
+
// block for seconds) off the monitor thread and coalesces rapid changes.
|
|
2117
|
+
std::thread network_recovery_thread_;
|
|
2118
|
+
std::mutex network_recovery_mutex_;
|
|
2119
|
+
std::condition_variable network_recovery_cv_;
|
|
2120
|
+
bool network_recovery_pending_ = false;
|
|
2121
|
+
bool network_recovery_stop_ = false;
|
|
2122
|
+
|
|
2123
|
+
void start_network_monitor();
|
|
2124
|
+
void stop_network_monitor();
|
|
2125
|
+
void network_recovery_loop();
|
|
2126
|
+
void handle_network_change(const std::vector<std::string>& current_addresses);
|
|
2127
|
+
void recover_after_network_change();
|
|
2128
|
+
|
|
1985
2129
|
#ifdef RATS_STORAGE
|
|
1986
2130
|
// Distributed storage manager (optional, requires RATS_STORAGE)
|
|
1987
2131
|
std::unique_ptr<StorageManager> storage_manager_;
|
|
@@ -43,6 +43,19 @@ bool RatsClient::start_dht_discovery(int dht_port) {
|
|
|
43
43
|
dht_client_v6_.reset();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// Forward the DHT's UDP port through the router, in addition to the TCP peer
|
|
47
|
+
// port mapped at start(). Like libtorrent (which maps both the TCP listen port
|
|
48
|
+
// and the UDP DHT/uTP port per listen socket), this makes the node reachable by
|
|
49
|
+
// inbound DHT traffic behind NAT. No-op when port mapping is disabled; the
|
|
50
|
+
// mapping uses external==internal so outgoing queries and inbound packets share
|
|
51
|
+
// one public port. The backends wake their workers to install it immediately.
|
|
52
|
+
// Use the *actual* bound port: start() may have fallen back to an ephemeral one
|
|
53
|
+
// when the requested dht_port was taken.
|
|
54
|
+
uint16_t bound_dht_port = dht_client_->get_port();
|
|
55
|
+
if (bound_dht_port != 0) {
|
|
56
|
+
add_port_mapping(PortMapProtocol::UDP, bound_dht_port);
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
// Start automatic peer discovery (this thread also performs the best-effort STUN probe
|
|
47
60
|
// that derives our BEP 42 node ID — see automatic_discovery_loop).
|
|
48
61
|
start_automatic_peer_discovery();
|
|
@@ -321,8 +334,14 @@ void RatsClient::announce_rats_peer() {
|
|
|
321
334
|
}
|
|
322
335
|
|
|
323
336
|
std::string discovery_hash = get_discovery_hash();
|
|
324
|
-
|
|
325
|
-
|
|
337
|
+
|
|
338
|
+
// Advertise the mapped public TCP port when UPnP/NAT-PMP has established one,
|
|
339
|
+
// so WAN peers connect to (external_ip, external_port) rather than our NATed
|
|
340
|
+
// local listen port. Falls back to listen_port_ when no mapping is active.
|
|
341
|
+
// (Mirrors libtorrent feeding the port-mapping result into what it announces.)
|
|
342
|
+
uint16_t announce_port = get_advertised_port();
|
|
343
|
+
LOG_CLIENT_INFO("Announcing peer for discovery hash: " << discovery_hash << " on port " << announce_port);
|
|
344
|
+
|
|
326
345
|
InfoHash info_hash = hex_to_node_id(discovery_hash);
|
|
327
346
|
|
|
328
347
|
// Skip only if every running DHT network already has this announce in flight.
|
|
@@ -336,7 +355,7 @@ void RatsClient::announce_rats_peer() {
|
|
|
336
355
|
|
|
337
356
|
// Use announce with callback - combines announce and find_peers in one traversal
|
|
338
357
|
// Peers discovered during traversal will be returned through the callback
|
|
339
|
-
if (announce_for_hash(discovery_hash,
|
|
358
|
+
if (announce_for_hash(discovery_hash, announce_port, [this, info_hash](const std::vector<std::string>& peer_addresses) {
|
|
340
359
|
LOG_CLIENT_INFO("Announce discovered " << peer_addresses.size() << " peers during traversal");
|
|
341
360
|
|
|
342
361
|
// Convert peer addresses to Peer objects for handle_dht_peer_discovery()
|
|
@@ -79,8 +79,14 @@ bool RatsClient::load_configuration() {
|
|
|
79
79
|
if (config.contains("encryption_enabled")) {
|
|
80
80
|
encryption_enabled_ = config.value("encryption_enabled", true);
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
|
|
83
|
+
// Load automatic port forwarding preference
|
|
84
|
+
if (config.contains("port_mapping_enabled")) {
|
|
85
|
+
std::lock_guard<std::mutex> pm_lock(port_mapping_mutex_);
|
|
86
|
+
port_mapping_config_.enabled = config.value("port_mapping_enabled", true);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
84
90
|
// Update last_updated timestamp
|
|
85
91
|
auto now = std::chrono::high_resolution_clock::now();
|
|
86
92
|
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
|
@@ -119,6 +125,10 @@ bool RatsClient::save_configuration() {
|
|
|
119
125
|
config["listen_port"] = listen_port_;
|
|
120
126
|
config["max_peers"] = max_peers_;
|
|
121
127
|
config["encryption_enabled"] = encryption_enabled_;
|
|
128
|
+
{
|
|
129
|
+
std::lock_guard<std::mutex> pm_lock(port_mapping_mutex_);
|
|
130
|
+
config["port_mapping_enabled"] = port_mapping_config_.enabled;
|
|
131
|
+
}
|
|
122
132
|
// TODO: Re-add when implementing new Noise protocol
|
|
123
133
|
// config["encryption_key"] = get_encryption_key();
|
|
124
134
|
|