librats 0.3.1 → 0.5.1

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 (69) hide show
  1. package/README.md +405 -405
  2. package/binding.gyp +96 -95
  3. package/lib/index.d.ts +522 -522
  4. package/lib/index.js +82 -82
  5. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  7. package/native-src/CMakeLists.txt +360 -0
  8. package/native-src/LICENSE +21 -0
  9. package/native-src/src/bencode.cpp +485 -0
  10. package/native-src/src/bencode.h +145 -0
  11. package/native-src/src/bittorrent.cpp +3682 -0
  12. package/native-src/src/bittorrent.h +731 -0
  13. package/native-src/src/dht.cpp +2342 -0
  14. package/native-src/src/dht.h +501 -0
  15. package/native-src/src/encrypted_socket.cpp +817 -0
  16. package/native-src/src/encrypted_socket.h +239 -0
  17. package/native-src/src/file_transfer.cpp +1808 -0
  18. package/native-src/src/file_transfer.h +567 -0
  19. package/native-src/src/fs.cpp +639 -0
  20. package/native-src/src/fs.h +108 -0
  21. package/native-src/src/gossipsub.cpp +1137 -0
  22. package/native-src/src/gossipsub.h +403 -0
  23. package/native-src/src/ice.cpp +1386 -0
  24. package/native-src/src/ice.h +328 -0
  25. package/native-src/src/json.hpp +25526 -0
  26. package/native-src/src/krpc.cpp +558 -0
  27. package/native-src/src/krpc.h +145 -0
  28. package/native-src/src/librats.cpp +2715 -0
  29. package/native-src/src/librats.h +1729 -0
  30. package/native-src/src/librats_bittorrent.cpp +167 -0
  31. package/native-src/src/librats_c.cpp +1317 -0
  32. package/native-src/src/librats_c.h +237 -0
  33. package/native-src/src/librats_encryption.cpp +123 -0
  34. package/native-src/src/librats_file_transfer.cpp +226 -0
  35. package/native-src/src/librats_gossipsub.cpp +293 -0
  36. package/native-src/src/librats_ice.cpp +515 -0
  37. package/native-src/src/librats_logging.cpp +158 -0
  38. package/native-src/src/librats_mdns.cpp +171 -0
  39. package/native-src/src/librats_nat.cpp +571 -0
  40. package/native-src/src/librats_persistence.cpp +815 -0
  41. package/native-src/src/logger.h +412 -0
  42. package/native-src/src/mdns.cpp +1178 -0
  43. package/native-src/src/mdns.h +253 -0
  44. package/native-src/src/network_utils.cpp +598 -0
  45. package/native-src/src/network_utils.h +162 -0
  46. package/native-src/src/noise.cpp +981 -0
  47. package/native-src/src/noise.h +227 -0
  48. package/native-src/src/os.cpp +371 -0
  49. package/native-src/src/os.h +40 -0
  50. package/native-src/src/rats_export.h +17 -0
  51. package/native-src/src/sha1.cpp +163 -0
  52. package/native-src/src/sha1.h +42 -0
  53. package/native-src/src/socket.cpp +1376 -0
  54. package/native-src/src/socket.h +309 -0
  55. package/native-src/src/stun.cpp +484 -0
  56. package/native-src/src/stun.h +349 -0
  57. package/native-src/src/threadmanager.cpp +105 -0
  58. package/native-src/src/threadmanager.h +53 -0
  59. package/native-src/src/tracker.cpp +1110 -0
  60. package/native-src/src/tracker.h +268 -0
  61. package/native-src/src/version.cpp +24 -0
  62. package/native-src/src/version.h.in +45 -0
  63. package/native-src/version.rc.in +31 -0
  64. package/package.json +62 -68
  65. package/scripts/build-librats.js +241 -194
  66. package/scripts/postinstall.js +52 -52
  67. package/scripts/prepare-package.js +187 -91
  68. package/scripts/verify-installation.js +119 -119
  69. package/src/librats_node.cpp +1174 -1174
@@ -0,0 +1,731 @@
1
+ #pragma once
2
+
3
+ #include "bencode.h"
4
+ #include "sha1.h"
5
+ #include "socket.h"
6
+ #include "dht.h"
7
+ #include "logger.h"
8
+ #include <string>
9
+ #include <vector>
10
+ #include <map>
11
+ #include <unordered_map>
12
+ #include <unordered_set>
13
+ #include <memory>
14
+ #include <functional>
15
+ #include <mutex>
16
+ #include <thread>
17
+ #include <atomic>
18
+ #include <chrono>
19
+ #include <array>
20
+ #include <algorithm> // Add this for std::all_of
21
+ #include <condition_variable>
22
+
23
+ namespace librats {
24
+
25
+ // Forward declarations
26
+ class BitTorrentClient;
27
+ class TorrentDownload;
28
+ class PeerConnection;
29
+ class MetadataDownload;
30
+ class TrackerManager;
31
+
32
+ //=============================================================================
33
+ // LOCK ORDERING DOCUMENTATION
34
+ //=============================================================================
35
+ // To prevent deadlocks, always acquire locks in the following order:
36
+ //
37
+ // BitTorrentClient level:
38
+ // torrents_mutex_ -> metadata_mutex_
39
+ //
40
+ // TorrentDownload level:
41
+ // peers_mutex_ -> pieces_mutex_ -> files_mutex_
42
+ //
43
+ // PeerConnection level:
44
+ // requests_mutex_ (independent, only used within PeerConnection)
45
+ //
46
+ // MetadataDownload level:
47
+ // mutex_ (never call callbacks while holding this mutex!)
48
+ //
49
+ // Cross-class lock ordering:
50
+ // BitTorrentClient::torrents_mutex_
51
+ // -> TorrentDownload::peers_mutex_
52
+ // -> TorrentDownload::pieces_mutex_
53
+ // -> TorrentDownload::files_mutex_
54
+ // -> PeerConnection::requests_mutex_
55
+ //=============================================================================
56
+
57
+ // Type aliases
58
+ using InfoHash = std::array<uint8_t, 20>;
59
+ using PieceIndex = uint32_t;
60
+ using BlockIndex = uint32_t;
61
+ using PeerID = std::array<uint8_t, 20>;
62
+
63
+ // Constants
64
+ constexpr size_t BLOCK_SIZE = 16384; // 16KB standard block size
65
+ constexpr size_t MAX_PIECE_SIZE = 2 * 1024 * 1024; // 2MB max piece size
66
+ constexpr size_t HANDSHAKE_TIMEOUT_MS = 30000; // 30 seconds
67
+ constexpr size_t REQUEST_TIMEOUT_MS = 60000; // 60 seconds
68
+ constexpr size_t MAX_REQUESTS_PER_PEER = 10; // Maximum concurrent requests per peer
69
+ constexpr size_t MAX_PEERS_PER_TORRENT = 50; // Maximum peers per torrent
70
+
71
+ // BitTorrent protocol constants
72
+ constexpr uint8_t BITTORRENT_PROTOCOL_ID[] = "BitTorrent protocol";
73
+ constexpr size_t BITTORRENT_PROTOCOL_ID_LENGTH = 19;
74
+
75
+ // File information structure
76
+ struct FileInfo {
77
+ std::string path;
78
+ uint64_t length;
79
+ uint64_t offset; // Offset within the torrent
80
+
81
+ FileInfo(const std::string& p, uint64_t len, uint64_t off)
82
+ : path(p), length(len), offset(off) {}
83
+ };
84
+
85
+ // Piece information
86
+ struct PieceInfo {
87
+ PieceIndex index;
88
+ std::array<uint8_t, 20> hash;
89
+ uint32_t length;
90
+ bool verified;
91
+ std::vector<bool> blocks_downloaded; // Track which blocks are downloaded (received)
92
+ std::vector<bool> blocks_requested; // Track which blocks are requested globally (sent but not received)
93
+ std::vector<uint8_t> data;
94
+
95
+ PieceInfo(PieceIndex idx, const std::array<uint8_t, 20>& h, uint32_t len)
96
+ : index(idx), hash(h), length(len), verified(false) {
97
+ uint32_t num_blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE;
98
+ blocks_downloaded.resize(num_blocks, false);
99
+ blocks_requested.resize(num_blocks, false);
100
+ // Don't pre-allocate data - allocate lazily when downloading starts
101
+ }
102
+
103
+ bool is_complete() const {
104
+ return std::all_of(blocks_downloaded.begin(), blocks_downloaded.end(), [](bool b) { return b; });
105
+ }
106
+
107
+ uint32_t get_num_blocks() const {
108
+ return static_cast<uint32_t>(blocks_downloaded.size());
109
+ }
110
+
111
+ // Check if all blocks have been requested (or downloaded)
112
+ bool all_blocks_requested() const {
113
+ for (size_t i = 0; i < blocks_downloaded.size(); ++i) {
114
+ if (!blocks_downloaded[i] && !blocks_requested[i]) {
115
+ return false;
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+
121
+ // Reset requested status for blocks that weren't received (for timeout/disconnect)
122
+ void reset_unreceived_requests() {
123
+ for (size_t i = 0; i < blocks_requested.size(); ++i) {
124
+ if (blocks_requested[i] && !blocks_downloaded[i]) {
125
+ blocks_requested[i] = false;
126
+ }
127
+ }
128
+ }
129
+
130
+ // Ensure data buffer is allocated
131
+ void ensure_data_allocated() {
132
+ if (data.empty()) {
133
+ data.resize(length);
134
+ }
135
+ }
136
+
137
+ // Free data buffer after piece is written to disk
138
+ void free_data() {
139
+ data.clear();
140
+ data.shrink_to_fit();
141
+ }
142
+ };
143
+
144
+ // Torrent file parser and information holder
145
+ class TorrentInfo {
146
+ public:
147
+ TorrentInfo();
148
+ ~TorrentInfo();
149
+
150
+ // Parse torrent from file
151
+ bool load_from_file(const std::string& torrent_file);
152
+
153
+ // Parse torrent from bencode data
154
+ bool load_from_bencode(const BencodeValue& torrent_data);
155
+
156
+ // Parse torrent from raw data
157
+ bool load_from_data(const std::vector<uint8_t>& data);
158
+
159
+ // Getters
160
+ const InfoHash& get_info_hash() const { return info_hash_; }
161
+ const std::string& get_name() const { return name_; }
162
+ uint64_t get_total_length() const { return total_length_; }
163
+ uint32_t get_piece_length() const { return piece_length_; }
164
+ uint32_t get_num_pieces() const { return static_cast<uint32_t>(piece_hashes_.size()); }
165
+ const std::vector<std::array<uint8_t, 20>>& get_piece_hashes() const { return piece_hashes_; }
166
+ const std::vector<FileInfo>& get_files() const { return files_; }
167
+ const std::string& get_announce() const { return announce_; }
168
+ const std::vector<std::string>& get_announce_list() const { return announce_list_; }
169
+ bool is_single_file() const { return files_.size() == 1; }
170
+ bool is_private() const { return private_; }
171
+
172
+ // Calculate piece length for specific piece
173
+ uint32_t get_piece_length(PieceIndex piece_index) const;
174
+
175
+ // Validate torrent info
176
+ bool is_valid() const;
177
+
178
+ // Create a minimal torrent info for metadata exchange only (BEP 9)
179
+ static TorrentInfo create_for_metadata_exchange(const InfoHash& info_hash);
180
+
181
+ // Check if this is a metadata-only torrent (for BEP 9 metadata exchange)
182
+ bool is_metadata_only() const { return metadata_only_; }
183
+
184
+ private:
185
+ InfoHash info_hash_;
186
+ std::string name_;
187
+ uint64_t total_length_;
188
+ uint32_t piece_length_;
189
+ std::vector<std::array<uint8_t, 20>> piece_hashes_;
190
+ std::vector<FileInfo> files_;
191
+ std::string announce_;
192
+ std::vector<std::string> announce_list_;
193
+ bool private_;
194
+ bool metadata_only_; // True if this is a placeholder for metadata exchange
195
+
196
+ bool parse_info_dict(const BencodeValue& info_dict);
197
+ void calculate_info_hash(const BencodeValue& info_dict);
198
+ void build_file_list(const BencodeValue& info_dict);
199
+ };
200
+
201
+ // BitTorrent peer wire protocol message types
202
+ enum class MessageType : uint8_t {
203
+ CHOKE = 0,
204
+ UNCHOKE = 1,
205
+ INTERESTED = 2,
206
+ NOT_INTERESTED = 3,
207
+ HAVE = 4,
208
+ BITFIELD = 5,
209
+ REQUEST = 6,
210
+ PIECE = 7,
211
+ CANCEL = 8,
212
+ PORT = 9,
213
+ EXTENDED = 20
214
+ };
215
+
216
+ // Extended message types (BEP 10)
217
+ enum class ExtendedMessageType : uint8_t {
218
+ HANDSHAKE = 0,
219
+ UT_METADATA = 1 // Our local ID for ut_metadata extension
220
+ };
221
+
222
+ // Metadata message types (BEP 9)
223
+ enum class MetadataMessageType : uint8_t {
224
+ REQUEST = 0,
225
+ DATA = 1,
226
+ REJECT = 2
227
+ };
228
+
229
+ // Metadata constants
230
+ constexpr size_t METADATA_PIECE_SIZE = 16384; // 16KB metadata pieces
231
+ constexpr size_t MAX_METADATA_SIZE = 10 * 1024 * 1024; // 10MB max metadata size
232
+
233
+ // BitTorrent peer wire protocol messages
234
+ struct PeerMessage {
235
+ MessageType type;
236
+ std::vector<uint8_t> payload;
237
+
238
+ PeerMessage(MessageType t) : type(t) {}
239
+ PeerMessage(MessageType t, const std::vector<uint8_t>& p) : type(t), payload(p) {}
240
+
241
+ // Serialize message to wire format
242
+ std::vector<uint8_t> serialize() const;
243
+
244
+ // Create specific message types
245
+ static PeerMessage create_choke();
246
+ static PeerMessage create_unchoke();
247
+ static PeerMessage create_interested();
248
+ static PeerMessage create_not_interested();
249
+ static PeerMessage create_have(PieceIndex piece_index);
250
+ static PeerMessage create_bitfield(const std::vector<bool>& bitfield);
251
+ static PeerMessage create_request(PieceIndex piece_index, uint32_t offset, uint32_t length);
252
+ static PeerMessage create_piece(PieceIndex piece_index, uint32_t offset, const std::vector<uint8_t>& data);
253
+ static PeerMessage create_cancel(PieceIndex piece_index, uint32_t offset, uint32_t length);
254
+ static PeerMessage create_port(uint16_t port);
255
+ };
256
+
257
+ // Peer request tracking
258
+ struct PeerRequest {
259
+ PieceIndex piece_index;
260
+ uint32_t offset;
261
+ uint32_t length;
262
+ std::chrono::steady_clock::time_point requested_at;
263
+
264
+ PeerRequest(PieceIndex piece, uint32_t off, uint32_t len)
265
+ : piece_index(piece), offset(off), length(len),
266
+ requested_at(std::chrono::steady_clock::now()) {}
267
+ };
268
+
269
+ // Peer connection state
270
+ enum class PeerState {
271
+ CONNECTING,
272
+ HANDSHAKING,
273
+ CONNECTED,
274
+ DISCONNECTED,
275
+ ERROR
276
+ };
277
+
278
+ // Individual peer connection for BitTorrent protocol
279
+ class PeerConnection {
280
+ public:
281
+ PeerConnection(TorrentDownload* torrent, const Peer& peer_info, socket_t socket = INVALID_SOCKET_VALUE);
282
+ ~PeerConnection();
283
+
284
+ // Connection management
285
+ bool connect();
286
+ void disconnect();
287
+ bool is_connected() const { return state_.load() == PeerState::CONNECTED; }
288
+ PeerState get_state() const { return state_.load(); }
289
+
290
+ // Message handling
291
+ bool send_message(const PeerMessage& message);
292
+ void process_messages();
293
+
294
+ // Seeding support
295
+ void send_bitfield(); // Send our bitfield to peer after handshake
296
+
297
+ // Piece requests
298
+ bool request_piece_block(PieceIndex piece_index, uint32_t offset, uint32_t length);
299
+ bool is_block_requested(PieceIndex piece_index, uint32_t offset) const;
300
+ void cancel_request(PieceIndex piece_index, uint32_t offset, uint32_t length);
301
+ void cancel_all_requests();
302
+
303
+ // Peer state
304
+ bool is_choked() const { return peer_choked_; }
305
+ bool is_interested() const { return am_interested_; }
306
+ bool peer_is_interested() const { return peer_interested_; }
307
+ bool am_choking() const { return am_choking_; }
308
+
309
+ void set_interested(bool interested);
310
+ void set_choke(bool choke);
311
+
312
+ // Bitfield management
313
+ bool has_piece(PieceIndex piece_index) const;
314
+ const std::vector<bool>& get_bitfield() const { return peer_bitfield_; }
315
+ void update_bitfield(const std::vector<bool>& bitfield);
316
+
317
+ // Statistics
318
+ uint64_t get_downloaded() const { return downloaded_bytes_; }
319
+ uint64_t get_uploaded() const { return uploaded_bytes_; }
320
+ size_t get_pending_requests() const { return pending_requests_.size(); }
321
+
322
+ // Speed calculation (bytes per second)
323
+ double get_download_speed() const;
324
+ double get_upload_speed() const;
325
+ void update_speed_stats(); // Call periodically to update speed
326
+
327
+ // Peer info
328
+ const Peer& get_peer_info() const { return peer_info_; }
329
+ const PeerID& get_peer_id() const { return peer_id_; }
330
+
331
+ // Extension protocol (BEP 10)
332
+ bool supports_extensions() const { return supports_extensions_; }
333
+ bool supports_metadata_exchange() const { return supports_metadata_exchange_; }
334
+ uint8_t get_peer_metadata_extension_id() const { return peer_ut_metadata_id_; }
335
+
336
+ // Metadata exchange (BEP 9)
337
+ void request_metadata_piece(uint32_t piece_index);
338
+ size_t get_peer_metadata_size() const { return peer_metadata_size_; }
339
+
340
+ private:
341
+ TorrentDownload* torrent_;
342
+ Peer peer_info_;
343
+ socket_t socket_;
344
+ std::atomic<PeerState> state_; // Atomic to allow thread-safe reads without lock
345
+ std::thread connection_thread_;
346
+ std::atomic<bool> should_disconnect_;
347
+ bool handshake_completed_; // Track if handshake is already done (for incoming connections)
348
+
349
+ // Conditional variables for immediate shutdown
350
+ std::condition_variable shutdown_cv_;
351
+ std::mutex shutdown_mutex_;
352
+
353
+ // Peer state
354
+ PeerID peer_id_;
355
+ bool peer_choked_;
356
+ bool am_choked_;
357
+ bool peer_interested_;
358
+ bool am_interested_;
359
+ bool am_choking_;
360
+ std::vector<bool> peer_bitfield_;
361
+
362
+ // Request tracking
363
+ std::vector<PeerRequest> pending_requests_;
364
+ mutable std::mutex requests_mutex_;
365
+
366
+ // Statistics
367
+ std::atomic<uint64_t> downloaded_bytes_;
368
+ std::atomic<uint64_t> uploaded_bytes_;
369
+
370
+ // Speed tracking
371
+ mutable std::mutex speed_mutex_;
372
+ std::chrono::steady_clock::time_point last_speed_update_time_;
373
+ uint64_t last_speed_downloaded_;
374
+ uint64_t last_speed_uploaded_;
375
+ std::atomic<double> download_speed_; // bytes per second
376
+ std::atomic<double> upload_speed_; // bytes per second
377
+
378
+ // Message buffer
379
+ std::vector<uint8_t> message_buffer_;
380
+ size_t expected_message_length_;
381
+
382
+ // Extension protocol support (BEP 10)
383
+ bool supports_extensions_;
384
+ bool supports_metadata_exchange_;
385
+ uint8_t peer_ut_metadata_id_; // Peer's extension ID for ut_metadata
386
+ size_t peer_metadata_size_;
387
+
388
+ void connection_loop();
389
+ bool perform_handshake();
390
+ bool send_handshake();
391
+ bool receive_handshake();
392
+
393
+ // Message parsing
394
+ std::unique_ptr<PeerMessage> parse_message(const std::vector<uint8_t>& data);
395
+ void handle_message(const PeerMessage& message);
396
+
397
+ // Specific message handlers
398
+ void handle_choke();
399
+ void handle_unchoke();
400
+ void handle_interested();
401
+ void handle_not_interested();
402
+ void handle_have(const std::vector<uint8_t>& payload);
403
+ void handle_bitfield(const std::vector<uint8_t>& payload);
404
+ void handle_request(const std::vector<uint8_t>& payload);
405
+ void handle_piece(const std::vector<uint8_t>& payload);
406
+ void handle_cancel(const std::vector<uint8_t>& payload);
407
+ void handle_extended(const std::vector<uint8_t>& payload);
408
+
409
+ // Extended message handlers (BEP 10)
410
+ void handle_extended_handshake(const std::vector<uint8_t>& payload);
411
+ void handle_metadata_message(const std::vector<uint8_t>& payload);
412
+ void send_extended_handshake();
413
+
414
+ // Utility
415
+ void cleanup_expired_requests();
416
+ bool read_data(std::vector<uint8_t>& buffer, size_t length);
417
+ bool write_data(const std::vector<uint8_t>& data);
418
+ };
419
+
420
+ // Download progress callback types
421
+ using ProgressCallback = std::function<void(uint64_t downloaded, uint64_t total, double percentage)>;
422
+ using PieceCompleteCallback = std::function<void(PieceIndex piece_index)>;
423
+ using TorrentCompleteCallback = std::function<void(const std::string& torrent_name)>;
424
+ using PeerConnectedCallback = std::function<void(const Peer& peer)>;
425
+ using PeerDisconnectedCallback = std::function<void(const Peer& peer)>;
426
+ using MetadataCompleteCallback = std::function<void(const TorrentInfo& torrent_info)>;
427
+
428
+ // Metadata download coordinator (BEP 9)
429
+ class MetadataDownload {
430
+ public:
431
+ MetadataDownload(const InfoHash& info_hash, size_t metadata_size);
432
+ ~MetadataDownload();
433
+
434
+ // Store a metadata piece from a peer
435
+ bool store_metadata_piece(uint32_t piece_index, const std::vector<uint8_t>& data);
436
+
437
+ // Check if we have all metadata pieces
438
+ bool is_complete() const;
439
+
440
+ // Get the reconstructed metadata
441
+ std::vector<uint8_t> get_metadata() const;
442
+
443
+ // Verify metadata hash matches expected info hash
444
+ bool verify_metadata() const;
445
+
446
+ // Get next piece to request
447
+ uint32_t get_next_piece_to_request() const;
448
+
449
+ // Check if a piece is complete
450
+ bool is_piece_complete(uint32_t piece_index) const;
451
+
452
+ // Get metadata info
453
+ const InfoHash& get_info_hash() const { return info_hash_; }
454
+ size_t get_metadata_size() const { return metadata_size_; }
455
+ uint32_t get_num_pieces() const { return num_pieces_; }
456
+
457
+ // Set completion callback
458
+ void set_completion_callback(MetadataCompleteCallback callback) { completion_callback_ = callback; }
459
+
460
+ private:
461
+ InfoHash info_hash_;
462
+ size_t metadata_size_;
463
+ uint32_t num_pieces_;
464
+ std::vector<std::vector<uint8_t>> pieces_;
465
+ std::vector<bool> pieces_complete_;
466
+ bool completion_triggered_; // Ensures callback is only invoked once
467
+ mutable std::mutex mutex_;
468
+ MetadataCompleteCallback completion_callback_;
469
+
470
+ // Internal versions without lock (called when mutex is already held)
471
+ bool verify_metadata_unlocked() const;
472
+ std::vector<uint8_t> get_metadata_unlocked() const;
473
+ };
474
+
475
+ // Individual torrent download
476
+ class TorrentDownload {
477
+ public:
478
+ TorrentDownload(const TorrentInfo& torrent_info, const std::string& download_path);
479
+ ~TorrentDownload();
480
+
481
+ // Control
482
+ bool start();
483
+ void stop();
484
+ void pause();
485
+ void resume();
486
+ bool is_running() const { return running_; }
487
+ bool is_paused() const { return paused_; }
488
+ bool is_complete() const;
489
+
490
+ // Peer management
491
+ bool add_peer(const Peer& peer);
492
+ bool add_peer(const Peer& peer, socket_t existing_socket); // For incoming connections
493
+ void remove_peer(const Peer& peer);
494
+ size_t get_peer_count() const;
495
+ std::vector<Peer> get_connected_peers() const;
496
+
497
+ // Piece management
498
+ bool is_piece_complete(PieceIndex piece_index) const;
499
+ bool is_piece_downloading(PieceIndex piece_index) const;
500
+ std::vector<PieceIndex> get_available_pieces() const;
501
+ std::vector<PieceIndex> get_needed_pieces(const std::vector<bool>& peer_bitfield) const;
502
+
503
+ // Piece data handling
504
+ bool store_piece_block(PieceIndex piece_index, uint32_t offset, const std::vector<uint8_t>& data);
505
+ bool verify_piece(PieceIndex piece_index);
506
+ void write_piece_to_disk(PieceIndex piece_index);
507
+ bool read_piece_from_disk(PieceIndex piece_index, std::vector<uint8_t>& data); // For seeding
508
+ void reset_block_request(PieceIndex piece_index, uint32_t block_index); // Reset timed-out block request
509
+
510
+ // Statistics and progress
511
+ uint64_t get_downloaded_bytes() const;
512
+ uint64_t get_uploaded_bytes() const;
513
+ double get_progress_percentage() const;
514
+ uint32_t get_completed_pieces() const;
515
+ std::vector<bool> get_piece_bitfield() const;
516
+
517
+ // Speed tracking
518
+ double get_download_speed() const; // bytes per second
519
+ double get_upload_speed() const; // bytes per second
520
+ void update_speed_stats(); // Call periodically to update speed
521
+
522
+ // Callbacks
523
+ void set_progress_callback(ProgressCallback callback) { progress_callback_ = callback; }
524
+ void set_piece_complete_callback(PieceCompleteCallback callback) { piece_complete_callback_ = callback; }
525
+ void set_torrent_complete_callback(TorrentCompleteCallback callback) { torrent_complete_callback_ = callback; }
526
+ void set_peer_connected_callback(PeerConnectedCallback callback) { peer_connected_callback_ = callback; }
527
+ void set_peer_disconnected_callback(PeerDisconnectedCallback callback) { peer_disconnected_callback_ = callback; }
528
+ void set_metadata_complete_callback(MetadataCompleteCallback callback) { metadata_complete_callback_ = callback; }
529
+
530
+ // Torrent info access
531
+ const TorrentInfo& get_torrent_info() const { return torrent_info_; }
532
+ const std::string& get_download_path() const { return download_path_; }
533
+
534
+ // DHT integration
535
+ void announce_to_dht(DhtClient* dht_client);
536
+ void request_peers_from_dht(DhtClient* dht_client);
537
+
538
+ // Tracker integration
539
+ void announce_to_trackers();
540
+ void request_peers_from_trackers();
541
+ TrackerManager* get_tracker_manager() const { return tracker_manager_.get(); }
542
+
543
+ // Metadata download (BEP 9)
544
+ MetadataDownload* get_metadata_download() const { return metadata_download_.get(); }
545
+ void set_metadata_download(std::shared_ptr<MetadataDownload> metadata_download);
546
+
547
+ private:
548
+ TorrentInfo torrent_info_;
549
+ std::string download_path_;
550
+ std::atomic<bool> running_;
551
+ std::atomic<bool> paused_;
552
+
553
+ // Piece management
554
+ std::vector<std::unique_ptr<PieceInfo>> pieces_;
555
+ std::vector<bool> piece_completed_;
556
+ std::vector<bool> piece_downloading_;
557
+ mutable std::mutex pieces_mutex_;
558
+
559
+ // Peer connections
560
+ std::vector<std::unique_ptr<PeerConnection>> peer_connections_;
561
+ mutable std::mutex peers_mutex_;
562
+
563
+ // Download management
564
+ std::thread download_thread_;
565
+ std::thread peer_management_thread_;
566
+
567
+ // Conditional variables for immediate shutdown
568
+ std::condition_variable shutdown_cv_;
569
+ std::mutex shutdown_mutex_;
570
+
571
+ // File handling (using fs module, no persistent handles needed)
572
+ mutable std::mutex files_mutex_;
573
+
574
+ // Callbacks
575
+ ProgressCallback progress_callback_;
576
+ PieceCompleteCallback piece_complete_callback_;
577
+ TorrentCompleteCallback torrent_complete_callback_;
578
+ PeerConnectedCallback peer_connected_callback_;
579
+ PeerDisconnectedCallback peer_disconnected_callback_;
580
+ MetadataCompleteCallback metadata_complete_callback_;
581
+
582
+ // Statistics
583
+ std::atomic<uint64_t> total_downloaded_;
584
+ std::atomic<uint64_t> total_uploaded_;
585
+
586
+ // Speed tracking
587
+ mutable std::mutex speed_mutex_;
588
+ std::chrono::steady_clock::time_point last_speed_update_time_;
589
+ uint64_t last_speed_downloaded_;
590
+ uint64_t last_speed_uploaded_;
591
+ std::atomic<double> download_speed_; // bytes per second
592
+ std::atomic<double> upload_speed_; // bytes per second
593
+
594
+ // Progress tracking (for change detection in logging)
595
+ double last_logged_progress_;
596
+ std::chrono::steady_clock::time_point last_state_log_time_;
597
+
598
+ // Metadata download (BEP 9)
599
+ std::shared_ptr<MetadataDownload> metadata_download_;
600
+
601
+ // Tracker manager
602
+ std::unique_ptr<TrackerManager> tracker_manager_;
603
+ PeerID our_peer_id_;
604
+
605
+ void download_loop();
606
+ void peer_management_loop();
607
+ void schedule_piece_requests();
608
+ void cleanup_disconnected_peers();
609
+
610
+ // Seeding support
611
+ void notify_peers_have_piece(PieceIndex piece_index); // Broadcast HAVE to all peers
612
+
613
+ // File operations
614
+ bool open_files();
615
+ void close_files();
616
+ bool create_directory_structure();
617
+
618
+ // Piece selection strategy
619
+ std::vector<PieceIndex> select_pieces_for_download();
620
+ PieceIndex select_rarest_piece(const std::vector<bool>& available_pieces);
621
+
622
+ // Progress tracking
623
+ void update_progress();
624
+ void on_piece_completed(PieceIndex piece_index);
625
+ void check_torrent_completion();
626
+ void log_detailed_state(); // Log detailed torrent state for debugging
627
+ };
628
+
629
+ // Main BitTorrent client
630
+ class BitTorrentClient {
631
+ public:
632
+ BitTorrentClient();
633
+ ~BitTorrentClient();
634
+
635
+ // Client control
636
+ bool start(int listen_port = 0);
637
+ void stop();
638
+ bool is_running() const { return running_; }
639
+
640
+ // Torrent management
641
+ std::shared_ptr<TorrentDownload> add_torrent(const std::string& torrent_file, const std::string& download_path);
642
+ std::shared_ptr<TorrentDownload> add_torrent(const TorrentInfo& torrent_info, const std::string& download_path);
643
+ std::shared_ptr<TorrentDownload> add_torrent_by_hash(const InfoHash& info_hash, const std::string& download_path);
644
+ std::shared_ptr<TorrentDownload> add_torrent_by_hash(const std::string& info_hash_hex, const std::string& download_path);
645
+ bool remove_torrent(const InfoHash& info_hash);
646
+ std::shared_ptr<TorrentDownload> get_torrent(const InfoHash& info_hash);
647
+ std::vector<std::shared_ptr<TorrentDownload>> get_all_torrents();
648
+
649
+ // DHT integration
650
+ void set_dht_client(DhtClient* dht_client) { dht_client_ = dht_client; }
651
+ DhtClient* get_dht_client() const { return dht_client_; }
652
+
653
+ // Peer discovery from DHT
654
+ void discover_peers_for_torrent(const InfoHash& info_hash);
655
+ void announce_torrent_to_dht(const InfoHash& info_hash);
656
+
657
+ // Statistics
658
+ size_t get_active_torrents_count() const;
659
+ uint64_t get_total_downloaded() const;
660
+ uint64_t get_total_uploaded() const;
661
+
662
+ // Configuration
663
+ void set_max_connections_per_torrent(size_t max_connections) { max_connections_per_torrent_ = max_connections; }
664
+ void set_download_rate_limit(uint64_t bytes_per_second) { download_rate_limit_ = bytes_per_second; }
665
+ void set_upload_rate_limit(uint64_t bytes_per_second) { upload_rate_limit_ = bytes_per_second; }
666
+
667
+ // Callbacks for torrent events
668
+ void set_torrent_added_callback(std::function<void(const InfoHash&)> callback) { torrent_added_callback_ = callback; }
669
+ void set_torrent_completed_callback(std::function<void(const InfoHash&)> callback) { torrent_completed_callback_ = callback; }
670
+ void set_torrent_removed_callback(std::function<void(const InfoHash&)> callback) { torrent_removed_callback_ = callback; }
671
+
672
+ // Metadata download management (BEP 9)
673
+ std::shared_ptr<MetadataDownload> get_metadata_download(const InfoHash& info_hash);
674
+ void register_metadata_download(const InfoHash& info_hash, std::shared_ptr<MetadataDownload> metadata_download);
675
+ void complete_metadata_download(const InfoHash& info_hash, const TorrentInfo& torrent_info, const std::string& download_path);
676
+
677
+ // Get torrent metadata without downloading (BEP 9)
678
+ using MetadataRetrievalCallback = std::function<void(const TorrentInfo& torrent_info, bool success, const std::string& error_message)>;
679
+ void get_torrent_metadata_by_hash(const InfoHash& info_hash, MetadataRetrievalCallback callback);
680
+ void get_torrent_metadata_by_hash(const std::string& info_hash_hex, MetadataRetrievalCallback callback);
681
+
682
+ private:
683
+ std::atomic<bool> running_;
684
+ int listen_port_;
685
+ socket_t listen_socket_;
686
+
687
+ // Torrents
688
+ std::map<InfoHash, std::shared_ptr<TorrentDownload>> torrents_;
689
+ mutable std::mutex torrents_mutex_;
690
+
691
+ // Metadata downloads (BEP 9)
692
+ std::map<InfoHash, std::shared_ptr<MetadataDownload>> metadata_downloads_;
693
+ std::map<InfoHash, std::string> metadata_download_paths_; // Store download path for later
694
+ std::map<InfoHash, std::shared_ptr<TorrentDownload>> metadata_only_torrents_; // Temporary torrents for metadata retrieval
695
+ std::map<InfoHash, MetadataRetrievalCallback> metadata_retrieval_callbacks_; // Callbacks for metadata retrieval
696
+ mutable std::mutex metadata_mutex_;
697
+
698
+ // DHT integration
699
+ DhtClient* dht_client_;
700
+
701
+ // Networking
702
+ std::thread incoming_connections_thread_;
703
+
704
+ // Conditional variables for immediate shutdown
705
+ std::condition_variable shutdown_cv_;
706
+ std::mutex shutdown_mutex_;
707
+
708
+ // Configuration
709
+ size_t max_connections_per_torrent_;
710
+ uint64_t download_rate_limit_;
711
+ uint64_t upload_rate_limit_;
712
+
713
+ // Callbacks
714
+ std::function<void(const InfoHash&)> torrent_added_callback_;
715
+ std::function<void(const InfoHash&)> torrent_completed_callback_;
716
+ std::function<void(const InfoHash&)> torrent_removed_callback_;
717
+
718
+ void handle_incoming_connections();
719
+ void handle_incoming_connection(socket_t client_socket);
720
+ bool perform_incoming_handshake(socket_t socket, InfoHash& info_hash, PeerID& peer_id);
721
+ };
722
+
723
+ // Utility functions
724
+ InfoHash calculate_info_hash(const BencodeValue& info_dict);
725
+ std::string info_hash_to_hex(const InfoHash& hash);
726
+ InfoHash hex_to_info_hash(const std::string& hex);
727
+ PeerID generate_peer_id();
728
+ std::vector<uint8_t> create_handshake_message(const InfoHash& info_hash, const PeerID& peer_id);
729
+ bool parse_handshake_message(const std::vector<uint8_t>& data, InfoHash& info_hash, PeerID& peer_id);
730
+
731
+ } // namespace librats