librats 0.5.0 → 0.5.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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +1 -0
  3. package/lib/index.d.ts +2 -1
  4. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  5. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  6. package/native-src/CMakeLists.txt +360 -0
  7. package/native-src/LICENSE +21 -0
  8. package/native-src/src/bencode.cpp +485 -0
  9. package/native-src/src/bencode.h +145 -0
  10. package/native-src/src/bittorrent.cpp +3682 -0
  11. package/native-src/src/bittorrent.h +731 -0
  12. package/native-src/src/dht.cpp +2460 -0
  13. package/native-src/src/dht.h +508 -0
  14. package/native-src/src/encrypted_socket.cpp +817 -0
  15. package/native-src/src/encrypted_socket.h +239 -0
  16. package/native-src/src/file_transfer.cpp +1808 -0
  17. package/native-src/src/file_transfer.h +567 -0
  18. package/native-src/src/fs.cpp +639 -0
  19. package/native-src/src/fs.h +108 -0
  20. package/native-src/src/gossipsub.cpp +1137 -0
  21. package/native-src/src/gossipsub.h +403 -0
  22. package/native-src/src/ice.cpp +1386 -0
  23. package/native-src/src/ice.h +328 -0
  24. package/native-src/src/json.hpp +25526 -0
  25. package/native-src/src/krpc.cpp +558 -0
  26. package/native-src/src/krpc.h +145 -0
  27. package/native-src/src/librats.cpp +2735 -0
  28. package/native-src/src/librats.h +1732 -0
  29. package/native-src/src/librats_bittorrent.cpp +167 -0
  30. package/native-src/src/librats_c.cpp +1333 -0
  31. package/native-src/src/librats_c.h +239 -0
  32. package/native-src/src/librats_encryption.cpp +123 -0
  33. package/native-src/src/librats_file_transfer.cpp +226 -0
  34. package/native-src/src/librats_gossipsub.cpp +293 -0
  35. package/native-src/src/librats_ice.cpp +515 -0
  36. package/native-src/src/librats_logging.cpp +158 -0
  37. package/native-src/src/librats_mdns.cpp +171 -0
  38. package/native-src/src/librats_nat.cpp +571 -0
  39. package/native-src/src/librats_persistence.cpp +815 -0
  40. package/native-src/src/logger.h +412 -0
  41. package/native-src/src/mdns.cpp +1178 -0
  42. package/native-src/src/mdns.h +253 -0
  43. package/native-src/src/network_utils.cpp +598 -0
  44. package/native-src/src/network_utils.h +162 -0
  45. package/native-src/src/noise.cpp +981 -0
  46. package/native-src/src/noise.h +227 -0
  47. package/native-src/src/os.cpp +371 -0
  48. package/native-src/src/os.h +40 -0
  49. package/native-src/src/rats_export.h +17 -0
  50. package/native-src/src/sha1.cpp +163 -0
  51. package/native-src/src/sha1.h +42 -0
  52. package/native-src/src/socket.cpp +1376 -0
  53. package/native-src/src/socket.h +309 -0
  54. package/native-src/src/stun.cpp +484 -0
  55. package/native-src/src/stun.h +349 -0
  56. package/native-src/src/threadmanager.cpp +105 -0
  57. package/native-src/src/threadmanager.h +53 -0
  58. package/native-src/src/tracker.cpp +1110 -0
  59. package/native-src/src/tracker.h +268 -0
  60. package/native-src/src/version.cpp +24 -0
  61. package/native-src/src/version.h.in +45 -0
  62. package/native-src/version.rc.in +31 -0
  63. package/package.json +2 -8
  64. package/scripts/build-librats.js +59 -12
  65. package/scripts/prepare-package.js +133 -37
  66. package/src/librats_node.cpp +46 -1
@@ -0,0 +1,1110 @@
1
+ #include "tracker.h"
2
+ #include "bencode.h"
3
+ #include "logger.h"
4
+ #include <sstream>
5
+ #include <iomanip>
6
+ #include <random>
7
+ #include <algorithm>
8
+ #include <cstring>
9
+
10
+ #define LOG_TRACKER_DEBUG(message) LOG_DEBUG("tracker", message)
11
+ #define LOG_TRACKER_INFO(message) LOG_INFO("tracker", message)
12
+ #define LOG_TRACKER_WARN(message) LOG_WARN("tracker", message)
13
+ #define LOG_TRACKER_ERROR(message) LOG_ERROR("tracker", message)
14
+
15
+ namespace librats {
16
+
17
+ //=============================================================================
18
+ // Utility Functions
19
+ //=============================================================================
20
+
21
+ std::string tracker_event_to_string(TrackerEvent event) {
22
+ switch (event) {
23
+ case TrackerEvent::STARTED: return "started";
24
+ case TrackerEvent::STOPPED: return "stopped";
25
+ case TrackerEvent::COMPLETED: return "completed";
26
+ case TrackerEvent::NONE:
27
+ default: return "";
28
+ }
29
+ }
30
+
31
+ TrackerEvent string_to_tracker_event(const std::string& event_str) {
32
+ if (event_str == "started") return TrackerEvent::STARTED;
33
+ if (event_str == "stopped") return TrackerEvent::STOPPED;
34
+ if (event_str == "completed") return TrackerEvent::COMPLETED;
35
+ return TrackerEvent::NONE;
36
+ }
37
+
38
+ //=============================================================================
39
+ // HttpTrackerClient Implementation
40
+ //=============================================================================
41
+
42
+ HttpTrackerClient::HttpTrackerClient(const std::string& tracker_url)
43
+ : tracker_url_(tracker_url), interval_(1800), is_working_(true) {
44
+ LOG_TRACKER_INFO("Created HTTP tracker client for: " << tracker_url_);
45
+ }
46
+
47
+ HttpTrackerClient::~HttpTrackerClient() = default;
48
+
49
+ bool HttpTrackerClient::announce(const TrackerRequest& request, TrackerResponseCallback callback) {
50
+ LOG_TRACKER_INFO("Announcing to HTTP tracker: " << tracker_url_);
51
+
52
+ std::string announce_url = build_announce_url(request);
53
+
54
+ try {
55
+ std::vector<uint8_t> response_data = http_get(announce_url);
56
+
57
+ if (response_data.empty()) {
58
+ LOG_TRACKER_ERROR("Empty response from tracker: " << tracker_url_);
59
+ is_working_ = false;
60
+
61
+ TrackerResponse error_response;
62
+ error_response.success = false;
63
+ error_response.failure_reason = "Empty response from tracker";
64
+ if (callback) callback(error_response, tracker_url_);
65
+ return false;
66
+ }
67
+
68
+ TrackerResponse response = parse_response(response_data);
69
+
70
+ if (response.success) {
71
+ last_announce_time_ = std::chrono::steady_clock::now();
72
+ interval_ = response.interval;
73
+ if (!response.tracker_id.empty()) {
74
+ tracker_id_ = response.tracker_id;
75
+ }
76
+ is_working_ = true;
77
+
78
+ LOG_TRACKER_INFO("Announce successful. Peers: " << response.peers.size()
79
+ << ", Seeders: " << response.complete
80
+ << ", Leechers: " << response.incomplete);
81
+ } else {
82
+ LOG_TRACKER_ERROR("Tracker announce failed: " << response.failure_reason);
83
+ is_working_ = false;
84
+ }
85
+
86
+ if (callback) {
87
+ callback(response, tracker_url_);
88
+ }
89
+
90
+ return response.success;
91
+
92
+ } catch (const std::exception& e) {
93
+ LOG_TRACKER_ERROR("Exception during announce: " << e.what());
94
+ is_working_ = false;
95
+
96
+ TrackerResponse error_response;
97
+ error_response.success = false;
98
+ error_response.failure_reason = std::string("Exception: ") + e.what();
99
+ if (callback) callback(error_response, tracker_url_);
100
+
101
+ return false;
102
+ }
103
+ }
104
+
105
+ bool HttpTrackerClient::scrape(const std::vector<InfoHash>& info_hashes, TrackerResponseCallback callback) {
106
+ LOG_TRACKER_INFO("Scraping HTTP tracker: " << tracker_url_);
107
+
108
+ std::string scrape_url = build_scrape_url(info_hashes);
109
+ if (scrape_url.empty()) {
110
+ LOG_TRACKER_WARN("Cannot build scrape URL for tracker: " << tracker_url_);
111
+ return false;
112
+ }
113
+
114
+ try {
115
+ std::vector<uint8_t> response_data = http_get(scrape_url);
116
+
117
+ if (response_data.empty()) {
118
+ LOG_TRACKER_ERROR("Empty scrape response from tracker: " << tracker_url_);
119
+ return false;
120
+ }
121
+
122
+ TrackerResponse response = parse_response(response_data);
123
+
124
+ if (callback) {
125
+ callback(response, tracker_url_);
126
+ }
127
+
128
+ return response.success;
129
+
130
+ } catch (const std::exception& e) {
131
+ LOG_TRACKER_ERROR("Exception during scrape: " << e.what());
132
+ return false;
133
+ }
134
+ }
135
+
136
+ std::string HttpTrackerClient::build_announce_url(const TrackerRequest& request) {
137
+ std::ostringstream url;
138
+ url << tracker_url_;
139
+
140
+ // Add query separator
141
+ if (tracker_url_.find('?') == std::string::npos) {
142
+ url << "?";
143
+ } else {
144
+ url << "&";
145
+ }
146
+
147
+ // Required parameters
148
+ url << "info_hash=" << url_encode_binary(request.info_hash.data(), request.info_hash.size());
149
+ url << "&peer_id=" << url_encode_binary(request.peer_id.data(), request.peer_id.size());
150
+ url << "&port=" << request.port;
151
+ url << "&uploaded=" << request.uploaded;
152
+ url << "&downloaded=" << request.downloaded;
153
+ url << "&left=" << request.left;
154
+ url << "&compact=1"; // Request compact peer list (BEP 23)
155
+
156
+ // Optional parameters
157
+ if (request.event != TrackerEvent::NONE) {
158
+ url << "&event=" << tracker_event_to_string(request.event);
159
+ }
160
+
161
+ if (!request.ip.empty()) {
162
+ url << "&ip=" << url_encode(request.ip);
163
+ }
164
+
165
+ if (request.numwant > 0) {
166
+ url << "&numwant=" << request.numwant;
167
+ }
168
+
169
+ if (!request.tracker_id.empty()) {
170
+ url << "&trackerid=" << url_encode(request.tracker_id);
171
+ } else if (!tracker_id_.empty()) {
172
+ url << "&trackerid=" << url_encode(tracker_id_);
173
+ }
174
+
175
+ return url.str();
176
+ }
177
+
178
+ std::string HttpTrackerClient::build_scrape_url(const std::vector<InfoHash>& info_hashes) {
179
+ // Convert announce URL to scrape URL by replacing "announce" with "scrape"
180
+ std::string scrape_url = tracker_url_;
181
+
182
+ size_t announce_pos = scrape_url.find("announce");
183
+ if (announce_pos == std::string::npos) {
184
+ return ""; // Cannot build scrape URL
185
+ }
186
+
187
+ scrape_url.replace(announce_pos, 8, "scrape");
188
+
189
+ // Add info_hash parameters
190
+ bool first = true;
191
+ for (const auto& info_hash : info_hashes) {
192
+ if (first) {
193
+ scrape_url += "?";
194
+ first = false;
195
+ } else {
196
+ scrape_url += "&";
197
+ }
198
+ scrape_url += "info_hash=" + url_encode_binary(info_hash.data(), info_hash.size());
199
+ }
200
+
201
+ return scrape_url;
202
+ }
203
+
204
+ TrackerResponse HttpTrackerClient::parse_response(const std::vector<uint8_t>& data) {
205
+ TrackerResponse response;
206
+
207
+ try {
208
+ BencodeValue tracker_response = bencode::decode(data);
209
+
210
+ if (!tracker_response.is_dict()) {
211
+ response.failure_reason = "Invalid tracker response format";
212
+ return response;
213
+ }
214
+
215
+ // Check for failure reason
216
+ if (tracker_response.has_key("failure reason")) {
217
+ response.failure_reason = tracker_response["failure reason"].as_string();
218
+ response.success = false;
219
+ return response;
220
+ }
221
+
222
+ // Parse warning message
223
+ if (tracker_response.has_key("warning message")) {
224
+ response.warning_message = tracker_response["warning message"].as_string();
225
+ LOG_TRACKER_WARN("Tracker warning: " << response.warning_message);
226
+ }
227
+
228
+ // Parse interval
229
+ if (tracker_response.has_key("interval")) {
230
+ response.interval = static_cast<uint32_t>(tracker_response["interval"].as_integer());
231
+ }
232
+
233
+ // Parse min interval
234
+ if (tracker_response.has_key("min interval")) {
235
+ response.min_interval = static_cast<uint32_t>(tracker_response["min interval"].as_integer());
236
+ }
237
+
238
+ // Parse tracker ID
239
+ if (tracker_response.has_key("tracker id")) {
240
+ response.tracker_id = tracker_response["tracker id"].as_string();
241
+ }
242
+
243
+ // Parse complete (seeders)
244
+ if (tracker_response.has_key("complete")) {
245
+ response.complete = static_cast<uint32_t>(tracker_response["complete"].as_integer());
246
+ }
247
+
248
+ // Parse incomplete (leechers)
249
+ if (tracker_response.has_key("incomplete")) {
250
+ response.incomplete = static_cast<uint32_t>(tracker_response["incomplete"].as_integer());
251
+ }
252
+
253
+ // Parse peers
254
+ if (tracker_response.has_key("peers")) {
255
+ const auto& peers_value = tracker_response["peers"];
256
+
257
+ if (peers_value.is_string()) {
258
+ // Compact peer list (BEP 23)
259
+ response.peers = parse_compact_peers(peers_value.as_string());
260
+ } else if (peers_value.is_list()) {
261
+ // Dictionary peer list
262
+ response.peers = parse_dict_peers(peers_value);
263
+ }
264
+ }
265
+
266
+ response.success = true;
267
+
268
+ } catch (const std::exception& e) {
269
+ LOG_TRACKER_ERROR("Failed to parse tracker response: " << e.what());
270
+ response.failure_reason = std::string("Parse error: ") + e.what();
271
+ response.success = false;
272
+ }
273
+
274
+ return response;
275
+ }
276
+
277
+ std::vector<Peer> HttpTrackerClient::parse_compact_peers(const std::string& peer_data) {
278
+ std::vector<Peer> peers;
279
+
280
+ // Each peer is 6 bytes: 4 bytes IP + 2 bytes port
281
+ if (peer_data.length() % 6 != 0) {
282
+ LOG_TRACKER_WARN("Invalid compact peer list length: " << peer_data.length());
283
+ return peers;
284
+ }
285
+
286
+ for (size_t i = 0; i < peer_data.length(); i += 6) {
287
+ const uint8_t* peer_bytes = reinterpret_cast<const uint8_t*>(peer_data.data() + i);
288
+
289
+ // Extract IP address
290
+ std::ostringstream ip_stream;
291
+ ip_stream << static_cast<int>(peer_bytes[0]) << "."
292
+ << static_cast<int>(peer_bytes[1]) << "."
293
+ << static_cast<int>(peer_bytes[2]) << "."
294
+ << static_cast<int>(peer_bytes[3]);
295
+
296
+ // Extract port (big-endian)
297
+ uint16_t port = (peer_bytes[4] << 8) | peer_bytes[5];
298
+
299
+ peers.emplace_back(ip_stream.str(), port);
300
+ }
301
+
302
+ LOG_TRACKER_DEBUG("Parsed " << peers.size() << " compact peers");
303
+ return peers;
304
+ }
305
+
306
+ std::vector<Peer> HttpTrackerClient::parse_dict_peers(const BencodeValue& peers_list) {
307
+ std::vector<Peer> peers;
308
+
309
+ for (size_t i = 0; i < peers_list.size(); ++i) {
310
+ const auto& peer_dict = peers_list[i];
311
+
312
+ if (!peer_dict.is_dict()) {
313
+ continue;
314
+ }
315
+
316
+ if (!peer_dict.has_key("ip") || !peer_dict.has_key("port")) {
317
+ continue;
318
+ }
319
+
320
+ std::string ip = peer_dict["ip"].as_string();
321
+ uint16_t port = static_cast<uint16_t>(peer_dict["port"].as_integer());
322
+
323
+ peers.emplace_back(ip, port);
324
+ }
325
+
326
+ LOG_TRACKER_DEBUG("Parsed " << peers.size() << " dictionary peers");
327
+ return peers;
328
+ }
329
+
330
+ std::vector<uint8_t> HttpTrackerClient::http_get(const std::string& url) {
331
+ LOG_TRACKER_DEBUG("HTTP GET: " << url);
332
+
333
+ // Parse URL to extract host, port, and path
334
+ std::string protocol, host, path;
335
+ uint16_t port = 80;
336
+
337
+ size_t protocol_end = url.find("://");
338
+ if (protocol_end != std::string::npos) {
339
+ protocol = url.substr(0, protocol_end);
340
+ size_t host_start = protocol_end + 3;
341
+ size_t path_start = url.find('/', host_start);
342
+
343
+ if (path_start != std::string::npos) {
344
+ std::string host_port = url.substr(host_start, path_start - host_start);
345
+ path = url.substr(path_start);
346
+
347
+ size_t port_pos = host_port.find(':');
348
+ if (port_pos != std::string::npos) {
349
+ host = host_port.substr(0, port_pos);
350
+ port = static_cast<uint16_t>(std::stoi(host_port.substr(port_pos + 1)));
351
+ } else {
352
+ host = host_port;
353
+ port = (protocol == "https") ? 443 : 80;
354
+ }
355
+ } else {
356
+ host = url.substr(host_start);
357
+ path = "/";
358
+ }
359
+ } else {
360
+ LOG_TRACKER_ERROR("Invalid URL format: " << url);
361
+ return std::vector<uint8_t>();
362
+ }
363
+
364
+ // Create TCP connection
365
+ socket_t socket = create_tcp_client(host, port, 15000); // 15 second timeout
366
+ if (!is_valid_socket(socket)) {
367
+ LOG_TRACKER_ERROR("Failed to connect to tracker: " << host << ":" << port);
368
+ return std::vector<uint8_t>();
369
+ }
370
+
371
+ // Build HTTP request
372
+ std::ostringstream request;
373
+ request << "GET " << path << " HTTP/1.0\r\n";
374
+ request << "Host: " << host << "\r\n";
375
+ request << "User-Agent: librats/1.0\r\n";
376
+ request << "Accept: */*\r\n";
377
+ request << "Connection: close\r\n";
378
+ request << "\r\n";
379
+
380
+ std::string request_str = request.str();
381
+
382
+ // Send request
383
+ if (send_tcp_string(socket, request_str) <= 0) {
384
+ LOG_TRACKER_ERROR("Failed to send HTTP request");
385
+ close_socket(socket);
386
+ return std::vector<uint8_t>();
387
+ }
388
+
389
+ // Receive response
390
+ std::vector<uint8_t> response_data;
391
+ bool headers_complete = false;
392
+ size_t content_start = 0;
393
+
394
+ while (true) {
395
+ std::vector<uint8_t> chunk = receive_tcp_data(socket, 4096);
396
+ if (chunk.empty()) {
397
+ break; // Connection closed or error
398
+ }
399
+
400
+ response_data.insert(response_data.end(), chunk.begin(), chunk.end());
401
+
402
+ // Find end of headers
403
+ if (!headers_complete) {
404
+ std::string response_str(response_data.begin(), response_data.end());
405
+ size_t header_end = response_str.find("\r\n\r\n");
406
+ if (header_end != std::string::npos) {
407
+ headers_complete = true;
408
+ content_start = header_end + 4;
409
+ }
410
+ }
411
+ }
412
+
413
+ close_socket(socket);
414
+
415
+ if (!headers_complete || content_start >= response_data.size()) {
416
+ LOG_TRACKER_ERROR("Invalid HTTP response");
417
+ return std::vector<uint8_t>();
418
+ }
419
+
420
+ // Extract body
421
+ std::vector<uint8_t> body(response_data.begin() + content_start, response_data.end());
422
+
423
+ LOG_TRACKER_DEBUG("HTTP response body size: " << body.size() << " bytes");
424
+ return body;
425
+ }
426
+
427
+ std::string HttpTrackerClient::url_encode(const std::string& str) {
428
+ std::ostringstream escaped;
429
+ escaped.fill('0');
430
+ escaped << std::hex;
431
+
432
+ for (char c : str) {
433
+ // Keep alphanumeric and other safe characters
434
+ if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
435
+ escaped << c;
436
+ } else {
437
+ // Percent-encode
438
+ escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
439
+ }
440
+ }
441
+
442
+ return escaped.str();
443
+ }
444
+
445
+ std::string HttpTrackerClient::url_encode_binary(const uint8_t* data, size_t len) {
446
+ std::ostringstream escaped;
447
+ escaped.fill('0');
448
+ escaped << std::hex << std::uppercase;
449
+
450
+ for (size_t i = 0; i < len; ++i) {
451
+ uint8_t c = data[i];
452
+ // Keep alphanumeric and other safe characters
453
+ if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
454
+ escaped << static_cast<char>(c);
455
+ } else {
456
+ // Percent-encode
457
+ escaped << '%' << std::setw(2) << static_cast<int>(c);
458
+ }
459
+ }
460
+
461
+ return escaped.str();
462
+ }
463
+
464
+ //=============================================================================
465
+ // UdpTrackerClient Implementation
466
+ //=============================================================================
467
+
468
+ UdpTrackerClient::UdpTrackerClient(const std::string& tracker_url)
469
+ : tracker_url_(tracker_url), port_(0), socket_(INVALID_SOCKET_VALUE),
470
+ interval_(1800), is_working_(true), connection_id_(0) {
471
+
472
+ if (parse_url()) {
473
+ LOG_TRACKER_INFO("Created UDP tracker client for: " << hostname_ << ":" << port_);
474
+ } else {
475
+ LOG_TRACKER_ERROR("Failed to parse UDP tracker URL: " << tracker_url_);
476
+ is_working_ = false;
477
+ }
478
+ }
479
+
480
+ UdpTrackerClient::~UdpTrackerClient() {
481
+ std::lock_guard<std::mutex> lock(socket_mutex_);
482
+ if (is_valid_socket(socket_)) {
483
+ close_socket(socket_);
484
+ socket_ = INVALID_SOCKET_VALUE;
485
+ }
486
+ }
487
+
488
+ bool UdpTrackerClient::parse_url() {
489
+ // Parse udp://hostname:port
490
+ if (tracker_url_.substr(0, 6) != "udp://") {
491
+ return false;
492
+ }
493
+
494
+ std::string host_port = tracker_url_.substr(6);
495
+ size_t colon_pos = host_port.find(':');
496
+
497
+ if (colon_pos == std::string::npos) {
498
+ return false;
499
+ }
500
+
501
+ hostname_ = host_port.substr(0, colon_pos);
502
+
503
+ // Extract port and remove any path
504
+ std::string port_str = host_port.substr(colon_pos + 1);
505
+ size_t slash_pos = port_str.find('/');
506
+ if (slash_pos != std::string::npos) {
507
+ port_str = port_str.substr(0, slash_pos);
508
+ }
509
+
510
+ try {
511
+ port_ = static_cast<uint16_t>(std::stoi(port_str));
512
+ } catch (const std::exception&) {
513
+ return false;
514
+ }
515
+
516
+ return true;
517
+ }
518
+
519
+ bool UdpTrackerClient::connect() {
520
+ // Create UDP socket if needed (protected by mutex)
521
+ {
522
+ std::lock_guard<std::mutex> lock(socket_mutex_);
523
+ if (!is_valid_socket(socket_)) {
524
+ socket_ = create_udp_socket();
525
+ if (!is_valid_socket(socket_)) {
526
+ LOG_TRACKER_ERROR("Failed to create UDP socket for tracker");
527
+ return false;
528
+ }
529
+ }
530
+ }
531
+
532
+ LOG_TRACKER_DEBUG("Connecting to UDP tracker: " << hostname_ << ":" << port_);
533
+
534
+ uint32_t transaction_id = generate_transaction_id();
535
+ std::vector<uint8_t> connect_request = build_connect_request(transaction_id);
536
+
537
+ // Send connect request and receive response (send_request handles its own locking)
538
+ std::vector<uint8_t> response = send_request(connect_request, 15000);
539
+
540
+ if (response.empty()) {
541
+ LOG_TRACKER_ERROR("No response from UDP tracker");
542
+ return false;
543
+ }
544
+
545
+ if (!parse_connect_response(response, transaction_id)) {
546
+ LOG_TRACKER_ERROR("Failed to parse connect response");
547
+ return false;
548
+ }
549
+
550
+ // Connection is valid for 1 minute
551
+ connection_expire_time_ = std::chrono::steady_clock::now() + std::chrono::seconds(60);
552
+
553
+ LOG_TRACKER_INFO("Successfully connected to UDP tracker");
554
+ return true;
555
+ }
556
+
557
+ bool UdpTrackerClient::is_connection_valid() {
558
+ return connection_id_ != 0 &&
559
+ std::chrono::steady_clock::now() < connection_expire_time_;
560
+ }
561
+
562
+ bool UdpTrackerClient::announce(const TrackerRequest& request, TrackerResponseCallback callback) {
563
+ LOG_TRACKER_INFO("Announcing to UDP tracker: " << tracker_url_);
564
+
565
+ // Connect if needed
566
+ if (!is_connection_valid()) {
567
+ if (!connect()) {
568
+ is_working_ = false;
569
+
570
+ TrackerResponse error_response;
571
+ error_response.success = false;
572
+ error_response.failure_reason = "Failed to connect to UDP tracker";
573
+ if (callback) callback(error_response, tracker_url_);
574
+
575
+ return false;
576
+ }
577
+ }
578
+
579
+ uint32_t transaction_id = generate_transaction_id();
580
+ std::vector<uint8_t> announce_request = build_announce_request(request, transaction_id);
581
+
582
+ // Send announce request
583
+ std::vector<uint8_t> response = send_request(announce_request, 15000);
584
+
585
+ if (response.empty()) {
586
+ LOG_TRACKER_ERROR("No response from UDP tracker announce");
587
+ is_working_ = false;
588
+
589
+ TrackerResponse error_response;
590
+ error_response.success = false;
591
+ error_response.failure_reason = "No response from tracker";
592
+ if (callback) callback(error_response, tracker_url_);
593
+
594
+ return false;
595
+ }
596
+
597
+ // Check for error response
598
+ if (response.size() >= 8) {
599
+ uint32_t action = read_uint32_be(response.data());
600
+ if (action == ACTION_ERROR) {
601
+ std::string error_msg = parse_error_response(response);
602
+ LOG_TRACKER_ERROR("UDP tracker error: " << error_msg);
603
+ is_working_ = false;
604
+
605
+ TrackerResponse error_response;
606
+ error_response.success = false;
607
+ error_response.failure_reason = error_msg;
608
+ if (callback) callback(error_response, tracker_url_);
609
+
610
+ return false;
611
+ }
612
+ }
613
+
614
+ TrackerResponse tracker_response = parse_announce_response(response, transaction_id);
615
+
616
+ if (tracker_response.success) {
617
+ last_announce_time_ = std::chrono::steady_clock::now();
618
+ interval_ = tracker_response.interval;
619
+ is_working_ = true;
620
+
621
+ LOG_TRACKER_INFO("UDP announce successful. Peers: " << tracker_response.peers.size()
622
+ << ", Seeders: " << tracker_response.complete
623
+ << ", Leechers: " << tracker_response.incomplete);
624
+ } else {
625
+ LOG_TRACKER_ERROR("UDP tracker announce failed: " << tracker_response.failure_reason);
626
+ is_working_ = false;
627
+ }
628
+
629
+ if (callback) {
630
+ callback(tracker_response, tracker_url_);
631
+ }
632
+
633
+ return tracker_response.success;
634
+ }
635
+
636
+ bool UdpTrackerClient::scrape(const std::vector<InfoHash>& info_hashes, TrackerResponseCallback callback) {
637
+ LOG_TRACKER_INFO("Scraping UDP tracker: " << tracker_url_);
638
+
639
+ // Connect if needed
640
+ if (!is_connection_valid()) {
641
+ if (!connect()) {
642
+ return false;
643
+ }
644
+ }
645
+
646
+ uint32_t transaction_id = generate_transaction_id();
647
+ std::vector<uint8_t> scrape_request = build_scrape_request(info_hashes, transaction_id);
648
+
649
+ // Send scrape request
650
+ std::vector<uint8_t> response = send_request(scrape_request, 15000);
651
+
652
+ if (response.empty()) {
653
+ LOG_TRACKER_ERROR("No response from UDP tracker scrape");
654
+ return false;
655
+ }
656
+
657
+ TrackerResponse tracker_response = parse_scrape_response(response, transaction_id);
658
+
659
+ if (callback) {
660
+ callback(tracker_response, tracker_url_);
661
+ }
662
+
663
+ return tracker_response.success;
664
+ }
665
+
666
+ std::vector<uint8_t> UdpTrackerClient::send_request(const std::vector<uint8_t>& request, int timeout_ms) {
667
+ std::lock_guard<std::mutex> lock(socket_mutex_);
668
+
669
+ if (!is_valid_socket(socket_)) {
670
+ return std::vector<uint8_t>();
671
+ }
672
+
673
+ // Send request
674
+ if (send_udp_data_to(socket_, request, hostname_, port_) <= 0) {
675
+ return std::vector<uint8_t>();
676
+ }
677
+
678
+ // Receive response with timeout
679
+ std::vector<uint8_t> response = receive_udp_data_with_timeout(socket_, 2048, timeout_ms);
680
+
681
+ return response;
682
+ }
683
+
684
+ std::vector<uint8_t> UdpTrackerClient::build_connect_request(uint32_t transaction_id) {
685
+ std::vector<uint8_t> request(16);
686
+
687
+ // Protocol ID (64-bit)
688
+ write_int64_be(request.data(), PROTOCOL_ID);
689
+
690
+ // Action: connect (32-bit)
691
+ write_uint32_be(request.data() + 8, ACTION_CONNECT);
692
+
693
+ // Transaction ID (32-bit)
694
+ write_uint32_be(request.data() + 12, transaction_id);
695
+
696
+ return request;
697
+ }
698
+
699
+ std::vector<uint8_t> UdpTrackerClient::build_announce_request(const TrackerRequest& request, uint32_t transaction_id) {
700
+ std::vector<uint8_t> announce_req(98);
701
+
702
+ size_t offset = 0;
703
+
704
+ // Connection ID (64-bit)
705
+ write_int64_be(announce_req.data() + offset, connection_id_);
706
+ offset += 8;
707
+
708
+ // Action: announce (32-bit)
709
+ write_uint32_be(announce_req.data() + offset, ACTION_ANNOUNCE);
710
+ offset += 4;
711
+
712
+ // Transaction ID (32-bit)
713
+ write_uint32_be(announce_req.data() + offset, transaction_id);
714
+ offset += 4;
715
+
716
+ // Info hash (20 bytes)
717
+ std::memcpy(announce_req.data() + offset, request.info_hash.data(), 20);
718
+ offset += 20;
719
+
720
+ // Peer ID (20 bytes)
721
+ std::memcpy(announce_req.data() + offset, request.peer_id.data(), 20);
722
+ offset += 20;
723
+
724
+ // Downloaded (64-bit)
725
+ write_int64_be(announce_req.data() + offset, request.downloaded);
726
+ offset += 8;
727
+
728
+ // Left (64-bit)
729
+ write_int64_be(announce_req.data() + offset, request.left);
730
+ offset += 8;
731
+
732
+ // Uploaded (64-bit)
733
+ write_int64_be(announce_req.data() + offset, request.uploaded);
734
+ offset += 8;
735
+
736
+ // Event (32-bit)
737
+ write_uint32_be(announce_req.data() + offset, static_cast<uint32_t>(request.event));
738
+ offset += 4;
739
+
740
+ // IP address (32-bit, 0 for default)
741
+ write_uint32_be(announce_req.data() + offset, 0);
742
+ offset += 4;
743
+
744
+ // Key (32-bit, random)
745
+ write_uint32_be(announce_req.data() + offset, generate_transaction_id());
746
+ offset += 4;
747
+
748
+ // Num want (32-bit, -1 for default)
749
+ write_uint32_be(announce_req.data() + offset, request.numwant > 0 ? request.numwant : 50);
750
+ offset += 4;
751
+
752
+ // Port (16-bit)
753
+ announce_req[offset] = (request.port >> 8) & 0xFF;
754
+ announce_req[offset + 1] = request.port & 0xFF;
755
+
756
+ return announce_req;
757
+ }
758
+
759
+ std::vector<uint8_t> UdpTrackerClient::build_scrape_request(const std::vector<InfoHash>& info_hashes, uint32_t transaction_id) {
760
+ std::vector<uint8_t> scrape_req(16 + info_hashes.size() * 20);
761
+
762
+ size_t offset = 0;
763
+
764
+ // Connection ID (64-bit)
765
+ write_int64_be(scrape_req.data() + offset, connection_id_);
766
+ offset += 8;
767
+
768
+ // Action: scrape (32-bit)
769
+ write_uint32_be(scrape_req.data() + offset, ACTION_SCRAPE);
770
+ offset += 4;
771
+
772
+ // Transaction ID (32-bit)
773
+ write_uint32_be(scrape_req.data() + offset, transaction_id);
774
+ offset += 4;
775
+
776
+ // Info hashes (20 bytes each)
777
+ for (const auto& info_hash : info_hashes) {
778
+ std::memcpy(scrape_req.data() + offset, info_hash.data(), 20);
779
+ offset += 20;
780
+ }
781
+
782
+ return scrape_req;
783
+ }
784
+
785
+ bool UdpTrackerClient::parse_connect_response(const std::vector<uint8_t>& data, uint32_t expected_transaction_id) {
786
+ if (data.size() < 16) {
787
+ LOG_TRACKER_ERROR("Invalid connect response size: " << data.size());
788
+ return false;
789
+ }
790
+
791
+ uint32_t action = read_uint32_be(data.data());
792
+ uint32_t transaction_id = read_uint32_be(data.data() + 4);
793
+
794
+ if (action != ACTION_CONNECT) {
795
+ LOG_TRACKER_ERROR("Invalid action in connect response: " << action);
796
+ return false;
797
+ }
798
+
799
+ if (transaction_id != expected_transaction_id) {
800
+ LOG_TRACKER_ERROR("Transaction ID mismatch in connect response");
801
+ return false;
802
+ }
803
+
804
+ connection_id_ = read_int64_be(data.data() + 8);
805
+
806
+ LOG_TRACKER_DEBUG("Received connection ID: " << connection_id_);
807
+ return true;
808
+ }
809
+
810
+ TrackerResponse UdpTrackerClient::parse_announce_response(const std::vector<uint8_t>& data, uint32_t expected_transaction_id) {
811
+ TrackerResponse response;
812
+
813
+ if (data.size() < 20) {
814
+ response.failure_reason = "Invalid announce response size";
815
+ return response;
816
+ }
817
+
818
+ uint32_t action = read_uint32_be(data.data());
819
+ uint32_t transaction_id = read_uint32_be(data.data() + 4);
820
+
821
+ if (action != ACTION_ANNOUNCE) {
822
+ response.failure_reason = "Invalid action in announce response";
823
+ return response;
824
+ }
825
+
826
+ if (transaction_id != expected_transaction_id) {
827
+ response.failure_reason = "Transaction ID mismatch";
828
+ return response;
829
+ }
830
+
831
+ // Parse response fields
832
+ response.interval = read_uint32_be(data.data() + 8);
833
+ response.incomplete = read_uint32_be(data.data() + 12);
834
+ response.complete = read_uint32_be(data.data() + 16);
835
+
836
+ // Parse peer list (6 bytes per peer: 4 bytes IP + 2 bytes port)
837
+ size_t peers_offset = 20;
838
+ while (peers_offset + 6 <= data.size()) {
839
+ const uint8_t* peer_data = data.data() + peers_offset;
840
+
841
+ std::ostringstream ip_stream;
842
+ ip_stream << static_cast<int>(peer_data[0]) << "."
843
+ << static_cast<int>(peer_data[1]) << "."
844
+ << static_cast<int>(peer_data[2]) << "."
845
+ << static_cast<int>(peer_data[3]);
846
+
847
+ uint16_t port = (peer_data[4] << 8) | peer_data[5];
848
+
849
+ response.peers.emplace_back(ip_stream.str(), port);
850
+ peers_offset += 6;
851
+ }
852
+
853
+ response.success = true;
854
+ return response;
855
+ }
856
+
857
+ TrackerResponse UdpTrackerClient::parse_scrape_response(const std::vector<uint8_t>& data, uint32_t expected_transaction_id) {
858
+ TrackerResponse response;
859
+
860
+ if (data.size() < 8) {
861
+ response.failure_reason = "Invalid scrape response size";
862
+ return response;
863
+ }
864
+
865
+ uint32_t action = read_uint32_be(data.data());
866
+ uint32_t transaction_id = read_uint32_be(data.data() + 4);
867
+
868
+ if (action != ACTION_SCRAPE) {
869
+ response.failure_reason = "Invalid action in scrape response";
870
+ return response;
871
+ }
872
+
873
+ if (transaction_id != expected_transaction_id) {
874
+ response.failure_reason = "Transaction ID mismatch";
875
+ return response;
876
+ }
877
+
878
+ // Parse scrape data (12 bytes per torrent)
879
+ if (data.size() >= 20) {
880
+ response.complete = read_uint32_be(data.data() + 8);
881
+ response.downloaded = read_uint32_be(data.data() + 12);
882
+ response.incomplete = read_uint32_be(data.data() + 16);
883
+ }
884
+
885
+ response.success = true;
886
+ return response;
887
+ }
888
+
889
+ std::string UdpTrackerClient::parse_error_response(const std::vector<uint8_t>& data) {
890
+ if (data.size() < 8) {
891
+ return "Unknown error";
892
+ }
893
+
894
+ // Error message starts at offset 8
895
+ std::string error_msg(data.begin() + 8, data.end());
896
+ return error_msg;
897
+ }
898
+
899
+ uint32_t UdpTrackerClient::generate_transaction_id() {
900
+ static std::random_device rd;
901
+ static std::mt19937 gen(rd());
902
+ static std::uniform_int_distribution<uint32_t> dis;
903
+ return dis(gen);
904
+ }
905
+
906
+ uint32_t UdpTrackerClient::read_uint32_be(const uint8_t* data) {
907
+ return (static_cast<uint32_t>(data[0]) << 24) |
908
+ (static_cast<uint32_t>(data[1]) << 16) |
909
+ (static_cast<uint32_t>(data[2]) << 8) |
910
+ static_cast<uint32_t>(data[3]);
911
+ }
912
+
913
+ void UdpTrackerClient::write_uint32_be(uint8_t* data, uint32_t value) {
914
+ data[0] = (value >> 24) & 0xFF;
915
+ data[1] = (value >> 16) & 0xFF;
916
+ data[2] = (value >> 8) & 0xFF;
917
+ data[3] = value & 0xFF;
918
+ }
919
+
920
+ int64_t UdpTrackerClient::read_int64_be(const uint8_t* data) {
921
+ return (static_cast<int64_t>(data[0]) << 56) |
922
+ (static_cast<int64_t>(data[1]) << 48) |
923
+ (static_cast<int64_t>(data[2]) << 40) |
924
+ (static_cast<int64_t>(data[3]) << 32) |
925
+ (static_cast<int64_t>(data[4]) << 24) |
926
+ (static_cast<int64_t>(data[5]) << 16) |
927
+ (static_cast<int64_t>(data[6]) << 8) |
928
+ static_cast<int64_t>(data[7]);
929
+ }
930
+
931
+ void UdpTrackerClient::write_int64_be(uint8_t* data, int64_t value) {
932
+ data[0] = (value >> 56) & 0xFF;
933
+ data[1] = (value >> 48) & 0xFF;
934
+ data[2] = (value >> 40) & 0xFF;
935
+ data[3] = (value >> 32) & 0xFF;
936
+ data[4] = (value >> 24) & 0xFF;
937
+ data[5] = (value >> 16) & 0xFF;
938
+ data[6] = (value >> 8) & 0xFF;
939
+ data[7] = value & 0xFF;
940
+ }
941
+
942
+ //=============================================================================
943
+ // TrackerManager Implementation
944
+ //=============================================================================
945
+
946
+ TrackerManager::TrackerManager(const TorrentInfo& torrent_info)
947
+ : info_hash_(torrent_info.get_info_hash()), announce_interval_(1800) {
948
+
949
+ LOG_TRACKER_INFO("Creating tracker manager for torrent: " << torrent_info.get_name());
950
+
951
+ // Add primary announce URL
952
+ if (!torrent_info.get_announce().empty()) {
953
+ add_tracker(torrent_info.get_announce());
954
+ }
955
+
956
+ // Add announce list
957
+ for (const auto& tracker_url : torrent_info.get_announce_list()) {
958
+ if (tracker_url != torrent_info.get_announce()) {
959
+ add_tracker(tracker_url);
960
+ }
961
+ }
962
+
963
+ LOG_TRACKER_INFO("Tracker manager initialized with " << trackers_.size() << " trackers");
964
+ }
965
+
966
+ TrackerManager::~TrackerManager() = default;
967
+
968
+ bool TrackerManager::add_tracker(const std::string& tracker_url) {
969
+ if (tracker_url.empty()) {
970
+ return false;
971
+ }
972
+
973
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
974
+
975
+ // Check if tracker already exists
976
+ for (const auto& tracker : trackers_) {
977
+ if (tracker->get_url() == tracker_url) {
978
+ return false;
979
+ }
980
+ }
981
+
982
+ auto tracker_client = create_tracker_client(tracker_url);
983
+ if (tracker_client) {
984
+ trackers_.push_back(tracker_client);
985
+ LOG_TRACKER_INFO("Added tracker: " << tracker_url);
986
+ return true;
987
+ }
988
+
989
+ return false;
990
+ }
991
+
992
+ void TrackerManager::announce(const TrackerRequest& request, TrackerResponseCallback callback) {
993
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
994
+
995
+ LOG_TRACKER_INFO("Announcing to all trackers (" << trackers_.size() << " trackers)");
996
+
997
+ for (auto& tracker : trackers_) {
998
+ // Skip non-working trackers
999
+ if (!tracker->is_working()) {
1000
+ continue;
1001
+ }
1002
+
1003
+ // Announce in separate thread to avoid blocking
1004
+ std::thread([tracker, request, callback]() {
1005
+ tracker->announce(request, callback);
1006
+ }).detach();
1007
+ }
1008
+
1009
+ last_announce_time_ = std::chrono::steady_clock::now();
1010
+ }
1011
+
1012
+ void TrackerManager::announce_to_best(const TrackerRequest& request, TrackerResponseCallback callback) {
1013
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
1014
+
1015
+ // Sort trackers by priority
1016
+ sort_trackers_by_priority();
1017
+
1018
+ // Announce to first working tracker
1019
+ for (auto& tracker : trackers_) {
1020
+ if (tracker->is_working()) {
1021
+ LOG_TRACKER_INFO("Announcing to best tracker: " << tracker->get_url());
1022
+
1023
+ std::thread([tracker, request, callback]() {
1024
+ tracker->announce(request, callback);
1025
+ }).detach();
1026
+
1027
+ last_announce_time_ = std::chrono::steady_clock::now();
1028
+ return;
1029
+ }
1030
+ }
1031
+
1032
+ LOG_TRACKER_WARN("No working trackers available for announce");
1033
+ }
1034
+
1035
+ void TrackerManager::scrape(TrackerResponseCallback callback) {
1036
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
1037
+
1038
+ LOG_TRACKER_INFO("Scraping all trackers");
1039
+
1040
+ for (auto& tracker : trackers_) {
1041
+ if (!tracker->is_working()) {
1042
+ continue;
1043
+ }
1044
+
1045
+ std::thread([tracker, callback, this]() {
1046
+ tracker->scrape({info_hash_}, callback);
1047
+ }).detach();
1048
+ }
1049
+ }
1050
+
1051
+ size_t TrackerManager::get_working_tracker_count() const {
1052
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
1053
+
1054
+ size_t count = 0;
1055
+ for (const auto& tracker : trackers_) {
1056
+ if (tracker->is_working()) {
1057
+ ++count;
1058
+ }
1059
+ }
1060
+
1061
+ return count;
1062
+ }
1063
+
1064
+ std::vector<std::string> TrackerManager::get_tracker_urls() const {
1065
+ std::lock_guard<std::mutex> lock(trackers_mutex_);
1066
+
1067
+ std::vector<std::string> urls;
1068
+ for (const auto& tracker : trackers_) {
1069
+ urls.push_back(tracker->get_url());
1070
+ }
1071
+
1072
+ return urls;
1073
+ }
1074
+
1075
+ bool TrackerManager::should_announce() const {
1076
+ auto now = std::chrono::steady_clock::now();
1077
+ auto time_since_last = std::chrono::duration_cast<std::chrono::seconds>(now - last_announce_time_).count();
1078
+ return time_since_last >= announce_interval_;
1079
+ }
1080
+
1081
+ std::chrono::steady_clock::time_point TrackerManager::get_next_announce_time() const {
1082
+ return last_announce_time_ + std::chrono::seconds(announce_interval_);
1083
+ }
1084
+
1085
+ std::shared_ptr<TrackerClient> TrackerManager::create_tracker_client(const std::string& tracker_url) {
1086
+ if (tracker_url.substr(0, 4) == "http") {
1087
+ // HTTP or HTTPS tracker
1088
+ return std::make_shared<HttpTrackerClient>(tracker_url);
1089
+ } else if (tracker_url.substr(0, 6) == "udp://") {
1090
+ // UDP tracker
1091
+ return std::make_shared<UdpTrackerClient>(tracker_url);
1092
+ }
1093
+
1094
+ LOG_TRACKER_WARN("Unsupported tracker protocol: " << tracker_url);
1095
+ return nullptr;
1096
+ }
1097
+
1098
+ void TrackerManager::sort_trackers_by_priority() {
1099
+ // Sort: working trackers first, then by last announce time
1100
+ std::sort(trackers_.begin(), trackers_.end(),
1101
+ [](const std::shared_ptr<TrackerClient>& a, const std::shared_ptr<TrackerClient>& b) {
1102
+ if (a->is_working() != b->is_working()) {
1103
+ return a->is_working();
1104
+ }
1105
+ return a->get_last_announce_time() < b->get_last_announce_time();
1106
+ });
1107
+ }
1108
+
1109
+ } // namespace librats
1110
+