librats 0.7.2 → 0.8.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 +10 -2
- package/native-src/src/bt_network.cpp +296 -183
- package/native-src/src/bt_network.h +25 -5
- package/native-src/src/io_poller.cpp +910 -0
- package/native-src/src/io_poller.h +138 -0
- package/native-src/src/librats.cpp +809 -1236
- package/native-src/src/librats.h +146 -54
- package/native-src/src/librats_bittorrent.cpp +1 -5
- package/native-src/src/librats_c.cpp +1 -1
- package/native-src/src/librats_discovery.cpp +309 -0
- package/native-src/src/librats_encryption.cpp +130 -283
- package/native-src/src/librats_file_transfer.cpp +2 -6
- package/native-src/src/librats_gossipsub.cpp +1 -5
- package/native-src/src/librats_ice.cpp +1 -1
- package/native-src/src/librats_log_macros.h +36 -0
- package/native-src/src/librats_logging.cpp +1 -7
- package/native-src/src/librats_mdns.cpp +1 -11
- package/native-src/src/librats_persistence.cpp +1 -11
- package/native-src/src/librats_reconnection.cpp +2 -13
- package/native-src/src/librats_statistic.cpp +97 -0
- package/package.json +1 -1
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#include "librats.h"
|
|
2
|
+
#include "librats_log_macros.h"
|
|
3
|
+
#include "sha1.h"
|
|
4
|
+
|
|
5
|
+
namespace librats {
|
|
6
|
+
|
|
7
|
+
// =========================================================================
|
|
8
|
+
// Peer Discovery Methods
|
|
9
|
+
// =========================================================================
|
|
10
|
+
|
|
11
|
+
bool RatsClient::start_dht_discovery(int dht_port) {
|
|
12
|
+
if (dht_client_ && dht_client_->is_running()) {
|
|
13
|
+
LOG_CLIENT_WARN("DHT discovery is already running");
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
LOG_CLIENT_INFO("Starting DHT discovery on port " << dht_port <<
|
|
18
|
+
(bind_address_.empty() ? "" : " bound to " + bind_address_));
|
|
19
|
+
|
|
20
|
+
dht_client_ = std::make_unique<DhtClient>(dht_port, bind_address_, data_directory_);
|
|
21
|
+
if (!dht_client_->start()) {
|
|
22
|
+
LOG_CLIENT_ERROR("Failed to start DHT client");
|
|
23
|
+
dht_client_.reset();
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Bootstrap with default nodes
|
|
28
|
+
auto bootstrap_nodes = DhtClient::get_default_bootstrap_nodes();
|
|
29
|
+
if (!dht_client_->bootstrap(bootstrap_nodes)) {
|
|
30
|
+
LOG_CLIENT_WARN("Failed to bootstrap DHT");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Start automatic peer discovery
|
|
34
|
+
start_automatic_peer_discovery();
|
|
35
|
+
|
|
36
|
+
LOG_CLIENT_INFO("DHT discovery started successfully");
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void RatsClient::stop_dht_discovery() {
|
|
41
|
+
if (!dht_client_) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
LOG_CLIENT_INFO("Stopping DHT discovery");
|
|
46
|
+
|
|
47
|
+
// Stop automatic peer discovery
|
|
48
|
+
stop_automatic_peer_discovery();
|
|
49
|
+
|
|
50
|
+
dht_client_->stop();
|
|
51
|
+
dht_client_.reset();
|
|
52
|
+
LOG_CLIENT_INFO("DHT discovery stopped");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
bool RatsClient::find_peers_by_hash(const std::string& content_hash, std::function<void(const std::vector<std::string>&)> callback) {
|
|
56
|
+
if (!dht_client_ || !dht_client_->is_running()) {
|
|
57
|
+
LOG_CLIENT_ERROR("DHT client not running");
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (content_hash.length() != CONTENT_HASH_HEX_LENGTH) {
|
|
62
|
+
LOG_CLIENT_ERROR("Invalid content hash length: " << content_hash.length() << " (expected " << CONTENT_HASH_HEX_LENGTH << ")");
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
LOG_CLIENT_INFO("Finding peers for content hash: " << content_hash);
|
|
67
|
+
|
|
68
|
+
InfoHash info_hash = hex_to_node_id(content_hash);
|
|
69
|
+
|
|
70
|
+
return dht_client_->find_peers(info_hash, [this, callback](const std::vector<Peer>& peers, const InfoHash& info_hash) {
|
|
71
|
+
// Convert Peer to string addresses for callback
|
|
72
|
+
std::vector<std::string> peer_addresses;
|
|
73
|
+
for (const auto& peer : peers) {
|
|
74
|
+
peer_addresses.emplace_back(peer.ip + ":" + std::to_string(peer.port));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (callback) {
|
|
78
|
+
callback(peer_addresses);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
bool RatsClient::announce_for_hash(const std::string& content_hash, uint16_t port,
|
|
84
|
+
std::function<void(const std::vector<std::string>&)> callback) {
|
|
85
|
+
if (!dht_client_ || !dht_client_->is_running()) {
|
|
86
|
+
LOG_CLIENT_ERROR("DHT client not running");
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (content_hash.length() != CONTENT_HASH_HEX_LENGTH) {
|
|
91
|
+
LOG_CLIENT_ERROR("Invalid content hash length: " << content_hash.length() << " (expected " << CONTENT_HASH_HEX_LENGTH << ")");
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (port == 0) {
|
|
96
|
+
port = listen_port_;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
LOG_CLIENT_INFO("Announcing for content hash: " << content_hash << " on port " << port
|
|
100
|
+
<< (callback ? " with peer callback" : ""));
|
|
101
|
+
|
|
102
|
+
InfoHash info_hash = hex_to_node_id(content_hash);
|
|
103
|
+
|
|
104
|
+
// Create wrapper callback that converts Peer to string addresses (if callback provided)
|
|
105
|
+
PeerDiscoveryCallback peer_callback = nullptr;
|
|
106
|
+
if (callback) {
|
|
107
|
+
peer_callback = [callback](const std::vector<Peer>& peers, const InfoHash& hash) {
|
|
108
|
+
std::vector<std::string> peer_addresses;
|
|
109
|
+
peer_addresses.reserve(peers.size());
|
|
110
|
+
for (const auto& peer : peers) {
|
|
111
|
+
peer_addresses.push_back(peer.ip + ":" + std::to_string(peer.port));
|
|
112
|
+
}
|
|
113
|
+
callback(peer_addresses);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return dht_client_->announce_peer(info_hash, port, peer_callback);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
bool RatsClient::is_dht_running() const {
|
|
121
|
+
return dht_client_ && dht_client_->is_running();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
size_t RatsClient::get_dht_routing_table_size() const {
|
|
125
|
+
if (!dht_client_) {
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
return dht_client_->get_routing_table_size();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
void RatsClient::handle_dht_peer_discovery(const std::vector<Peer>& peers, const InfoHash& info_hash) {
|
|
132
|
+
LOG_CLIENT_INFO("DHT discovered " << peers.size() << " peers for info hash: " << node_id_to_hex(info_hash));
|
|
133
|
+
|
|
134
|
+
// Auto-connect to discovered peers
|
|
135
|
+
for (const auto& peer : peers) {
|
|
136
|
+
if (!can_connect_to_peer(peer.ip, peer.port)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
LOG_CLIENT_DEBUG("Attempting to connect to discovered peer: " << peer.ip << ":" << peer.port);
|
|
141
|
+
|
|
142
|
+
// Try to connect to the peer (non-blocking, managed thread for graceful shutdown)
|
|
143
|
+
add_managed_thread(std::thread([this, peer]() {
|
|
144
|
+
if (connect_to_peer(peer.ip, peer.port)) {
|
|
145
|
+
LOG_CLIENT_INFO("Successfully connected to DHT discovered peer: " << peer.ip << ":" << peer.port);
|
|
146
|
+
} else {
|
|
147
|
+
LOG_CLIENT_DEBUG("Failed to connect to DHT discovered peer: " << peer.ip << ":" << peer.port);
|
|
148
|
+
}
|
|
149
|
+
}), "dht-connect-" + peer.ip);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
void RatsClient::start_automatic_peer_discovery() {
|
|
154
|
+
if (auto_discovery_running_.load()) {
|
|
155
|
+
LOG_CLIENT_WARN("Automatic peer discovery is already running");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
LOG_CLIENT_INFO("Starting automatic rats peer discovery");
|
|
160
|
+
auto_discovery_running_.store(true);
|
|
161
|
+
auto_discovery_thread_ = std::thread(&RatsClient::automatic_discovery_loop, this);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
void RatsClient::stop_automatic_peer_discovery() {
|
|
165
|
+
if (!auto_discovery_running_.load()) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
LOG_CLIENT_INFO("Stopping automatic peer discovery");
|
|
170
|
+
auto_discovery_running_.store(false);
|
|
171
|
+
|
|
172
|
+
if (auto_discovery_thread_.joinable()) {
|
|
173
|
+
auto_discovery_thread_.join();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
LOG_CLIENT_INFO("Automatic peer discovery stopped");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
bool RatsClient::is_automatic_discovery_running() const {
|
|
180
|
+
return auto_discovery_running_.load();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
std::chrono::seconds RatsClient::calculate_discovery_interval() const {
|
|
184
|
+
int peer_count = get_peer_count();
|
|
185
|
+
|
|
186
|
+
// No peers - aggressive discovery
|
|
187
|
+
if (peer_count == 0) {
|
|
188
|
+
return std::chrono::seconds(15);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Calculate fill ratio
|
|
192
|
+
float fill_ratio = static_cast<float>(peer_count) / static_cast<float>(max_peers_);
|
|
193
|
+
|
|
194
|
+
// Graduated intervals based on fill ratio
|
|
195
|
+
if (fill_ratio < 0.25f) {
|
|
196
|
+
// Less than 25% full - still fairly aggressive
|
|
197
|
+
return std::chrono::seconds(60); // 1 minute
|
|
198
|
+
} else if (fill_ratio < 0.50f) {
|
|
199
|
+
// 25-50% full - moderate
|
|
200
|
+
return std::chrono::seconds(180); // 3 minutes
|
|
201
|
+
} else if (fill_ratio < 0.75f) {
|
|
202
|
+
// 50-75% full - relaxed
|
|
203
|
+
return std::chrono::seconds(600); // 10 minutes
|
|
204
|
+
} else {
|
|
205
|
+
// 75-100% full - very relaxed (mostly just re-announcing)
|
|
206
|
+
return std::chrono::seconds(1800); // 30 minutes
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void RatsClient::automatic_discovery_loop() {
|
|
211
|
+
LOG_CLIENT_INFO("Automatic peer discovery loop started");
|
|
212
|
+
|
|
213
|
+
// Initial delay to let DHT bootstrap
|
|
214
|
+
{
|
|
215
|
+
std::unique_lock<std::mutex> lock(shutdown_mutex_);
|
|
216
|
+
if (shutdown_cv_.wait_for(lock, std::chrono::seconds(INITIAL_DISCOVERY_DELAY_SECONDS), [this] { return !auto_discovery_running_.load() || !running_.load(); })) {
|
|
217
|
+
LOG_CLIENT_INFO("Automatic peer discovery loop stopped during initial delay");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Announce immediately - this also discovers peers during traversal
|
|
223
|
+
announce_rats_peer();
|
|
224
|
+
|
|
225
|
+
auto last_announce = std::chrono::steady_clock::now();
|
|
226
|
+
|
|
227
|
+
while (auto_discovery_running_.load()) {
|
|
228
|
+
auto now = std::chrono::steady_clock::now();
|
|
229
|
+
|
|
230
|
+
// Announce combines both announcing our presence and discovering peers
|
|
231
|
+
// Interval scales based on peer count: aggressive when empty, relaxed when nearly full
|
|
232
|
+
auto interval = calculate_discovery_interval();
|
|
233
|
+
|
|
234
|
+
if (now - last_announce >= interval) {
|
|
235
|
+
LOG_CLIENT_DEBUG("Discovery interval: " << interval.count() << "s (peers: "
|
|
236
|
+
<< get_peer_count() << "/" << max_peers_ << ")");
|
|
237
|
+
announce_rats_peer();
|
|
238
|
+
last_announce = now;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Use conditional variable for responsive shutdown
|
|
242
|
+
{
|
|
243
|
+
std::unique_lock<std::mutex> lock(shutdown_mutex_);
|
|
244
|
+
if (shutdown_cv_.wait_for(lock, std::chrono::milliseconds(500), [this] { return !auto_discovery_running_.load() || !running_.load(); })) {
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
LOG_CLIENT_INFO("Automatic peer discovery loop stopped");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void RatsClient::announce_rats_peer() {
|
|
254
|
+
if (!dht_client_ || !dht_client_->is_running()) {
|
|
255
|
+
LOG_CLIENT_WARN("DHT client not running, cannot announce peer");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
std::string discovery_hash = get_discovery_hash();
|
|
260
|
+
LOG_CLIENT_INFO("Announcing peer for discovery hash: " << discovery_hash << " on port " << listen_port_);
|
|
261
|
+
|
|
262
|
+
InfoHash info_hash = hex_to_node_id(discovery_hash);
|
|
263
|
+
|
|
264
|
+
if (dht_client_->is_announce_active(info_hash)) {
|
|
265
|
+
LOG_CLIENT_WARN("Announce already in progress for info hash: " << node_id_to_hex(info_hash));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Use announce with callback - combines announce and find_peers in one traversal
|
|
270
|
+
// Peers discovered during traversal will be returned through the callback
|
|
271
|
+
if (announce_for_hash(discovery_hash, listen_port_, [this, info_hash](const std::vector<std::string>& peer_addresses) {
|
|
272
|
+
LOG_CLIENT_INFO("Announce discovered " << peer_addresses.size() << " peers during traversal");
|
|
273
|
+
|
|
274
|
+
// Convert peer addresses to Peer objects for handle_dht_peer_discovery()
|
|
275
|
+
std::vector<Peer> peers;
|
|
276
|
+
peers.reserve(peer_addresses.size());
|
|
277
|
+
for (const auto& peer_address : peer_addresses) {
|
|
278
|
+
std::string ip;
|
|
279
|
+
int port;
|
|
280
|
+
if (parse_address_string(peer_address, ip, port)) {
|
|
281
|
+
peers.push_back(Peer(ip, port));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Auto-connect to discovered peers
|
|
286
|
+
if (!peers.empty()) {
|
|
287
|
+
handle_dht_peer_discovery(peers, info_hash);
|
|
288
|
+
}
|
|
289
|
+
})) {
|
|
290
|
+
LOG_CLIENT_DEBUG("Successfully started announce with peer discovery for discovery hash");
|
|
291
|
+
} else {
|
|
292
|
+
LOG_CLIENT_WARN("Failed to announce peer for discovery");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
std::string RatsClient::get_discovery_hash() const {
|
|
297
|
+
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
298
|
+
// Generate discovery hash based on current protocol configuration
|
|
299
|
+
std::string discovery_string = custom_protocol_name_ + "_peer_discovery_v" + custom_protocol_version_;
|
|
300
|
+
return SHA1::hash(discovery_string);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
std::string RatsClient::get_rats_peer_discovery_hash() {
|
|
304
|
+
// Well-known hash for rats peer discovery
|
|
305
|
+
// Compute SHA1 hash of "rats_peer_discovery_v1.0"
|
|
306
|
+
return SHA1::hash("rats_peer_discovery_v1.0");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
}
|