librats 1.0.2 → 2.2.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/README.md +145 -331
- package/binding.gyp +16 -3
- package/lib/index.d.ts +288 -696
- package/lib/index.js +407 -44
- package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
- package/native-src/CMakeLists.txt +404 -179
- package/native-src/LICENSE +1 -1
- package/native-src/src/librats/bindings/rats.cpp +762 -0
- package/native-src/src/librats/bindings/rats.h +380 -0
- package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
- package/native-src/src/librats/bittorrent/bencode.h +176 -0
- package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
- package/native-src/src/librats/bittorrent/bitfield.h +76 -0
- package/native-src/src/librats/bittorrent/byte_io.h +58 -0
- package/native-src/src/librats/bittorrent/choker.cpp +25 -0
- package/native-src/src/librats/bittorrent/choker.h +46 -0
- package/native-src/src/librats/bittorrent/client.cpp +413 -0
- package/native-src/src/librats/bittorrent/client.h +227 -0
- package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
- package/native-src/src/librats/bittorrent/disk_io.h +150 -0
- package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
- package/native-src/src/librats/bittorrent/extensions.h +94 -0
- package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
- package/native-src/src/librats/bittorrent/file_storage.h +79 -0
- package/native-src/src/librats/bittorrent/log.h +42 -0
- package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
- package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
- package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
- package/native-src/src/librats/bittorrent/peer_list.h +75 -0
- package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
- package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
- package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
- package/native-src/src/librats/bittorrent/reactor.h +89 -0
- package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
- package/native-src/src/librats/bittorrent/resume_data.h +41 -0
- package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
- package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
- package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
- package/native-src/src/librats/bittorrent/torrent.h +260 -0
- package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
- package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
- package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
- package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
- package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
- package/native-src/src/librats/bittorrent/tracker.h +108 -0
- package/native-src/src/librats/bittorrent/types.cpp +206 -0
- package/native-src/src/librats/bittorrent/types.h +86 -0
- package/native-src/src/librats/core/address.cpp +35 -0
- package/native-src/src/librats/core/address.h +78 -0
- package/native-src/src/librats/core/bytes.h +69 -0
- package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
- package/native-src/src/librats/core/chained_send_buffer.h +183 -0
- package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
- package/native-src/src/librats/core/endpoint_parse.h +31 -0
- package/native-src/src/librats/core/event_bus.h +70 -0
- package/native-src/src/librats/core/host_endpoint.h +56 -0
- package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
- package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
- package/native-src/src/librats/core/ip_address.cpp +120 -0
- package/native-src/src/librats/core/ip_address.h +109 -0
- package/native-src/src/librats/core/mpsc_queue.h +47 -0
- package/native-src/src/librats/core/notifier.h +74 -0
- package/native-src/src/librats/core/receive_buffer.cpp +219 -0
- package/native-src/src/librats/core/receive_buffer.h +171 -0
- package/native-src/src/librats/core/service_registry.h +58 -0
- package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
- package/native-src/src/librats/core/socket.h +496 -0
- package/native-src/src/librats/core/timer_queue.h +105 -0
- package/native-src/src/librats/core/types.cpp +43 -0
- package/native-src/src/librats/core/types.h +103 -0
- package/native-src/src/librats/core/wakeup_pipe.h +83 -0
- package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
- package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
- package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
- package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
- package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
- package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
- package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
- package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
- package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
- package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
- package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
- package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
- package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
- package/native-src/src/librats/crypto/hkdf.c +266 -0
- package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
- package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
- package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
- package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
- package/native-src/src/librats/crypto/poly1305.h +37 -0
- package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
- package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
- package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
- package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
- package/native-src/src/librats/dht/announce.cpp +37 -0
- package/native-src/src/librats/dht/announce.h +41 -0
- package/native-src/src/librats/dht/bep42.cpp +109 -0
- package/native-src/src/librats/dht/bep42.h +48 -0
- package/native-src/src/librats/dht/dht.cpp +501 -0
- package/native-src/src/librats/dht/dht.h +119 -0
- package/native-src/src/librats/dht/dht_runner.cpp +103 -0
- package/native-src/src/librats/dht/dht_runner.h +71 -0
- package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
- package/native-src/src/librats/dht/dos_blocker.h +47 -0
- package/native-src/src/librats/dht/find_peers.cpp +52 -0
- package/native-src/src/librats/dht/find_peers.h +73 -0
- package/native-src/src/librats/dht/id.h +167 -0
- package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
- package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
- package/native-src/src/librats/dht/log.h +38 -0
- package/native-src/src/librats/dht/node.cpp +473 -0
- package/native-src/src/librats/dht/node.h +164 -0
- package/native-src/src/librats/dht/node_entry.h +81 -0
- package/native-src/src/librats/dht/observer.h +72 -0
- package/native-src/src/librats/dht/persistence.cpp +90 -0
- package/native-src/src/librats/dht/persistence.h +32 -0
- package/native-src/src/librats/dht/routing_table.cpp +559 -0
- package/native-src/src/librats/dht/routing_table.h +185 -0
- package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
- package/native-src/src/librats/dht/rpc_manager.h +77 -0
- package/native-src/src/librats/dht/storage.cpp +92 -0
- package/native-src/src/librats/dht/storage.h +74 -0
- package/native-src/src/librats/dht/transport.h +27 -0
- package/native-src/src/librats/dht/traversal.cpp +326 -0
- package/native-src/src/librats/dht/traversal.h +120 -0
- package/native-src/src/librats/dht/udp_transport.cpp +49 -0
- package/native-src/src/librats/dht/udp_transport.h +51 -0
- package/native-src/src/librats/mdns/log.h +22 -0
- package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
- package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
- package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
- package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
- package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
- package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
- package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
- package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
- package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
- package/native-src/src/librats/node/circuit_service.h +84 -0
- package/native-src/src/librats/node/config.h +110 -0
- package/native-src/src/librats/node/dial_service.h +54 -0
- package/native-src/src/librats/node/dialer.cpp +264 -0
- package/native-src/src/librats/node/dialer.h +188 -0
- package/native-src/src/librats/node/host_events.h +26 -0
- package/native-src/src/librats/node/identify.cpp +130 -0
- package/native-src/src/librats/node/identify.h +71 -0
- package/native-src/src/librats/node/nat_status.cpp +103 -0
- package/native-src/src/librats/node/nat_status.h +118 -0
- package/native-src/src/librats/node/node.cpp +865 -0
- package/native-src/src/librats/node/node.h +344 -0
- package/native-src/src/librats/node/node_context.h +33 -0
- package/native-src/src/librats/node/peer_network.h +91 -0
- package/native-src/src/librats/peer/peer.h +49 -0
- package/native-src/src/librats/peer/peer_book.cpp +181 -0
- package/native-src/src/librats/peer/peer_book.h +88 -0
- package/native-src/src/librats/peer/peer_id.cpp +72 -0
- package/native-src/src/librats/peer/peer_id.h +62 -0
- package/native-src/src/librats/peer/peer_info.h +37 -0
- package/native-src/src/librats/peer/peer_table.cpp +170 -0
- package/native-src/src/librats/peer/peer_table.h +148 -0
- package/native-src/src/librats/security/handshaker.h +66 -0
- package/native-src/src/librats/security/identity.h +43 -0
- package/native-src/src/librats/security/noise_security.cpp +122 -0
- package/native-src/src/librats/security/noise_security.h +37 -0
- package/native-src/src/librats/security/plaintext_security.h +106 -0
- package/native-src/src/librats/security/session.h +37 -0
- package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
- package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
- package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
- package/native-src/src/librats/subsystems/bittorrent.h +136 -0
- package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
- package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
- package/native-src/src/librats/subsystems/dht_service.h +36 -0
- package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
- package/native-src/src/librats/subsystems/file_transfer.h +367 -0
- package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
- package/native-src/src/librats/subsystems/hole_punch.h +290 -0
- package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
- package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
- package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
- package/native-src/src/librats/subsystems/message_json.cpp +112 -0
- package/native-src/src/librats/subsystems/message_json.h +88 -0
- package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
- package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
- package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
- package/native-src/src/librats/subsystems/ping_service.h +66 -0
- package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
- package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
- package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
- package/native-src/src/librats/subsystems/pubsub.h +175 -0
- package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
- package/native-src/src/librats/subsystems/reconnection.h +126 -0
- package/native-src/src/librats/subsystems/relay.cpp +1142 -0
- package/native-src/src/librats/subsystems/relay.h +211 -0
- package/native-src/src/librats/subsystems/relay_service.h +46 -0
- package/native-src/src/librats/transport/connection.cpp +343 -0
- package/native-src/src/librats/transport/connection.h +283 -0
- package/native-src/src/librats/transport/link.h +96 -0
- package/native-src/src/librats/transport/reactor.cpp +588 -0
- package/native-src/src/librats/transport/reactor.h +262 -0
- package/native-src/src/librats/transport/reactor_pool.h +81 -0
- package/native-src/src/librats/transport/relay_link.cpp +208 -0
- package/native-src/src/librats/transport/relay_link.h +303 -0
- package/native-src/src/librats/transport/tcp_link.cpp +49 -0
- package/native-src/src/librats/transport/tcp_link.h +43 -0
- package/native-src/src/librats/transport/udp_mux.cpp +617 -0
- package/native-src/src/librats/transport/udp_mux.h +363 -0
- package/native-src/src/librats/transport/udp_packet.cpp +121 -0
- package/native-src/src/librats/transport/udp_packet.h +190 -0
- package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
- package/native-src/src/librats/transport/udp_stream.h +614 -0
- package/native-src/src/librats/util/features.h.in +51 -0
- package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
- package/native-src/src/librats/util/fs.h +136 -0
- package/native-src/src/librats/util/json.cpp +1002 -0
- package/native-src/src/librats/util/json.h +444 -0
- package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
- package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
- package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
- package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
- package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
- package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
- package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
- package/native-src/src/librats/util/rats_export.h +69 -0
- package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
- package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
- package/native-src/src/librats/wire/frame.cpp +76 -0
- package/native-src/src/librats/wire/frame.h +111 -0
- package/native-src/src/librats/wire/message_router.cpp +45 -0
- package/native-src/src/librats/wire/message_router.h +47 -0
- package/package.json +5 -4
- package/scripts/build-librats.js +1 -0
- package/scripts/postinstall.js +3 -3
- package/scripts/prepare-package.js +4 -4
- package/scripts/verify-installation.js +63 -105
- package/src/librats_node.cpp +1067 -1323
- package/native-src/src/bencode.cpp +0 -485
- package/native-src/src/bencode.h +0 -145
- package/native-src/src/bittorrent.cpp +0 -14
- package/native-src/src/bittorrent.h +0 -74
- package/native-src/src/bt_bitfield.cpp +0 -372
- package/native-src/src/bt_bitfield.h +0 -316
- package/native-src/src/bt_choker.cpp +0 -228
- package/native-src/src/bt_choker.h +0 -147
- package/native-src/src/bt_client.cpp +0 -1047
- package/native-src/src/bt_client.h +0 -445
- package/native-src/src/bt_create_torrent.cpp +0 -677
- package/native-src/src/bt_create_torrent.h +0 -473
- package/native-src/src/bt_extension.cpp +0 -469
- package/native-src/src/bt_extension.h +0 -309
- package/native-src/src/bt_file_storage.cpp +0 -261
- package/native-src/src/bt_file_storage.h +0 -298
- package/native-src/src/bt_handshake.cpp +0 -134
- package/native-src/src/bt_handshake.h +0 -157
- package/native-src/src/bt_messages.cpp +0 -364
- package/native-src/src/bt_messages.h +0 -324
- package/native-src/src/bt_network.cpp +0 -1007
- package/native-src/src/bt_network.h +0 -417
- package/native-src/src/bt_peer_connection.cpp +0 -742
- package/native-src/src/bt_peer_connection.h +0 -592
- package/native-src/src/bt_piece_picker.cpp +0 -786
- package/native-src/src/bt_piece_picker.h +0 -473
- package/native-src/src/bt_resume_data.cpp +0 -410
- package/native-src/src/bt_resume_data.h +0 -249
- package/native-src/src/bt_torrent.cpp +0 -2120
- package/native-src/src/bt_torrent.h +0 -641
- package/native-src/src/bt_torrent_info.cpp +0 -659
- package/native-src/src/bt_torrent_info.h +0 -418
- package/native-src/src/bt_types.h +0 -621
- package/native-src/src/chained_send_buffer.cpp +0 -75
- package/native-src/src/chained_send_buffer.h +0 -137
- package/native-src/src/crypto/hkdf.c +0 -266
- package/native-src/src/crypto/poly1305.h +0 -36
- package/native-src/src/dht.cpp +0 -3311
- package/native-src/src/dht.h +0 -717
- package/native-src/src/disk_io.cpp +0 -632
- package/native-src/src/disk_io.h +0 -315
- package/native-src/src/file_transfer.cpp +0 -1415
- package/native-src/src/file_transfer.h +0 -286
- package/native-src/src/fs.h +0 -108
- package/native-src/src/gossipsub.cpp +0 -1139
- package/native-src/src/gossipsub.h +0 -403
- package/native-src/src/ice.cpp +0 -893
- package/native-src/src/ice.h +0 -559
- package/native-src/src/json.hpp +0 -25526
- package/native-src/src/librats.cpp +0 -2378
- package/native-src/src/librats.h +0 -2324
- package/native-src/src/librats_bittorrent.cpp +0 -601
- package/native-src/src/librats_c.cpp +0 -1557
- package/native-src/src/librats_c.h +0 -323
- package/native-src/src/librats_discovery.cpp +0 -402
- package/native-src/src/librats_encryption.cpp +0 -275
- package/native-src/src/librats_file_transfer.cpp +0 -144
- package/native-src/src/librats_gossipsub.cpp +0 -289
- package/native-src/src/librats_ice.cpp +0 -213
- package/native-src/src/librats_log_macros.h +0 -36
- package/native-src/src/librats_logging.cpp +0 -173
- package/native-src/src/librats_mdns.cpp +0 -166
- package/native-src/src/librats_persistence.cpp +0 -796
- package/native-src/src/librats_portmap.cpp +0 -419
- package/native-src/src/librats_reconnection.cpp +0 -218
- package/native-src/src/librats_statistic.cpp +0 -105
- package/native-src/src/librats_storage.cpp +0 -189
- package/native-src/src/rats_export.h +0 -17
- package/native-src/src/receive_buffer.cpp +0 -82
- package/native-src/src/receive_buffer.h +0 -127
- package/native-src/src/socket.h +0 -228
- package/native-src/src/threadmanager.cpp +0 -105
- package/native-src/src/threadmanager.h +0 -53
- package/native-src/src/tracker.cpp +0 -1264
- package/native-src/src/tracker.h +0 -319
- package/native-src/src/turn.cpp +0 -762
- package/native-src/src/turn.h +0 -460
- package/native-src/src/wakeup_pipe.h +0 -60
- /package/native-src/src/{os.h → librats/util/os.h} +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#include "librats/bittorrent/tracker.h"
|
|
2
|
+
|
|
3
|
+
#include "librats/bittorrent/bencode.h"
|
|
4
|
+
#include "librats/bittorrent/byte_io.h"
|
|
5
|
+
#include "librats/bittorrent/log.h"
|
|
6
|
+
#include "librats/core/socket.h"
|
|
7
|
+
#include "librats/util/network_utils.h"
|
|
8
|
+
|
|
9
|
+
#include <algorithm>
|
|
10
|
+
#include <cctype>
|
|
11
|
+
#include <cstring>
|
|
12
|
+
#include <iomanip>
|
|
13
|
+
#include <random>
|
|
14
|
+
#include <sstream>
|
|
15
|
+
#include <thread>
|
|
16
|
+
|
|
17
|
+
namespace librats::bittorrent {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
// UDP BEP 15 constants.
|
|
22
|
+
constexpr std::int64_t kUdpMagic = 0x41727101980LL;
|
|
23
|
+
constexpr std::uint32_t kActionConnect = 0;
|
|
24
|
+
constexpr std::uint32_t kActionAnnounce = 1;
|
|
25
|
+
constexpr std::uint32_t kActionError = 3;
|
|
26
|
+
|
|
27
|
+
std::uint32_t random_u32() {
|
|
28
|
+
thread_local std::mt19937 gen(std::random_device{}());
|
|
29
|
+
return std::uniform_int_distribution<std::uint32_t>{}(gen);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Parse a TCP/UDP port out of an untrusted tracker URL. Returns 0 on anything
|
|
33
|
+
// that is not a plain 1..65535 decimal number. std::stoi must NOT be used here:
|
|
34
|
+
// a hostile ".torrent"/magnet `tr=udp://host:notaport` would throw
|
|
35
|
+
// std::invalid_argument out of the detached announce worker → std::terminate.
|
|
36
|
+
std::uint16_t parse_port(const std::string& s) {
|
|
37
|
+
if (s.empty()) return 0;
|
|
38
|
+
std::uint32_t v = 0;
|
|
39
|
+
for (const char c : s) {
|
|
40
|
+
if (c < '0' || c > '9') return 0;
|
|
41
|
+
v = v * 10 + std::uint32_t(c - '0');
|
|
42
|
+
if (v > 65535) return 0;
|
|
43
|
+
}
|
|
44
|
+
return std::uint16_t(v);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const char* http_event(TrackerEvent e) {
|
|
48
|
+
switch (e) {
|
|
49
|
+
case TrackerEvent::Started: return "started";
|
|
50
|
+
case TrackerEvent::Stopped: return "stopped";
|
|
51
|
+
case TrackerEvent::Completed: return "completed";
|
|
52
|
+
default: return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// UDP event codes differ from our enum order, so map explicitly.
|
|
57
|
+
std::uint32_t udp_event(TrackerEvent e) {
|
|
58
|
+
switch (e) {
|
|
59
|
+
case TrackerEvent::Completed: return 1;
|
|
60
|
+
case TrackerEvent::Started: return 2;
|
|
61
|
+
case TrackerEvent::Stopped: return 3;
|
|
62
|
+
default: return 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
std::string url_encode_binary(const std::uint8_t* data, std::size_t len) {
|
|
67
|
+
std::ostringstream o;
|
|
68
|
+
o.fill('0');
|
|
69
|
+
o << std::hex;
|
|
70
|
+
for (std::size_t i = 0; i < len; ++i) {
|
|
71
|
+
const std::uint8_t c = data[i];
|
|
72
|
+
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') o << char(c);
|
|
73
|
+
else o << '%' << std::setw(2) << int(c);
|
|
74
|
+
}
|
|
75
|
+
return o.str();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std::vector<Address> parse_compact_peers(const std::string& data) {
|
|
79
|
+
std::vector<Address> peers;
|
|
80
|
+
for (std::size_t i = 0; i + 6 <= data.size(); i += 6) {
|
|
81
|
+
const auto* b = reinterpret_cast<const std::uint8_t*>(data.data() + i);
|
|
82
|
+
std::ostringstream ip;
|
|
83
|
+
ip << int(b[0]) << '.' << int(b[1]) << '.' << int(b[2]) << '.' << int(b[3]);
|
|
84
|
+
peers.emplace_back(ip.str(), read_u16_be(b + 4));
|
|
85
|
+
}
|
|
86
|
+
return peers;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
std::vector<Address> parse_dict_peers(const librats::BencodeValue& list) {
|
|
90
|
+
std::vector<Address> peers;
|
|
91
|
+
for (std::size_t i = 0; i < list.size(); ++i) {
|
|
92
|
+
const auto& d = list[i];
|
|
93
|
+
if (!d.is_dict() || !d.has_key("ip") || !d.has_key("port")) continue;
|
|
94
|
+
const std::uint16_t port = std::uint16_t(d["port"].as_integer());
|
|
95
|
+
if (port == 0) continue;
|
|
96
|
+
// BEP 3 dictionary model: "ip" may be a dotted-quad, an IPv6 literal, OR a
|
|
97
|
+
// DNS name. Address is strictly numeric, so a name must be resolved here
|
|
98
|
+
// (we already run on a blocking announce worker) — feeding it to the numeric
|
|
99
|
+
// Address ctor would assert in debug and silently drop the peer in release.
|
|
100
|
+
const std::string& host = d["ip"].as_string();
|
|
101
|
+
auto ip = IpAddress::parse(host);
|
|
102
|
+
if (!ip) {
|
|
103
|
+
std::string resolved = network_utils::resolve_hostname(host);
|
|
104
|
+
if (resolved.empty()) resolved = network_utils::resolve_hostname_v6(host);
|
|
105
|
+
ip = IpAddress::parse(resolved);
|
|
106
|
+
}
|
|
107
|
+
if (ip) peers.emplace_back(*ip, port);
|
|
108
|
+
}
|
|
109
|
+
return peers;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Blocking HTTP/1.0 GET; returns the response body (headers stripped).
|
|
113
|
+
Bytes http_get(const std::string& url, int timeout_ms) {
|
|
114
|
+
const std::size_t scheme = url.find("://");
|
|
115
|
+
if (scheme == std::string::npos) return {};
|
|
116
|
+
const std::string proto = url.substr(0, scheme);
|
|
117
|
+
const std::size_t host_start = scheme + 3;
|
|
118
|
+
const std::size_t path_start = url.find('/', host_start);
|
|
119
|
+
|
|
120
|
+
std::string host_port = url.substr(host_start, path_start == std::string::npos ? std::string::npos
|
|
121
|
+
: path_start - host_start);
|
|
122
|
+
std::string path = (path_start == std::string::npos) ? "/" : url.substr(path_start);
|
|
123
|
+
|
|
124
|
+
std::uint16_t port = (proto == "https") ? 443 : 80;
|
|
125
|
+
if (const std::size_t colon = host_port.find(':'); colon != std::string::npos) {
|
|
126
|
+
const std::uint16_t parsed = parse_port(host_port.substr(colon + 1));
|
|
127
|
+
if (parsed == 0) return {}; // malformed port — fail the announce, never crash
|
|
128
|
+
port = parsed;
|
|
129
|
+
host_port = host_port.substr(0, colon);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
socket_t sock = create_tcp_client(host_port, port, timeout_ms);
|
|
133
|
+
if (!is_valid_socket(sock)) return {};
|
|
134
|
+
|
|
135
|
+
std::ostringstream req;
|
|
136
|
+
req << "GET " << path << " HTTP/1.0\r\nHost: " << host_port
|
|
137
|
+
<< "\r\nUser-Agent: librats\r\nAccept: */*\r\nConnection: close\r\n\r\n";
|
|
138
|
+
if (send_tcp_string(sock, req.str()) <= 0) { close_socket(sock); return {}; }
|
|
139
|
+
|
|
140
|
+
Bytes data;
|
|
141
|
+
for (;;) {
|
|
142
|
+
Bytes chunk = receive_tcp_data(sock, 4096);
|
|
143
|
+
if (chunk.empty()) break;
|
|
144
|
+
data.insert(data.end(), chunk.begin(), chunk.end());
|
|
145
|
+
}
|
|
146
|
+
close_socket(sock);
|
|
147
|
+
|
|
148
|
+
// Strip headers (everything up to and including the blank line).
|
|
149
|
+
static const std::uint8_t sep[4] = {'\r', '\n', '\r', '\n'};
|
|
150
|
+
for (std::size_t i = 0; i + 4 <= data.size(); ++i) {
|
|
151
|
+
if (std::memcmp(data.data() + i, sep, 4) == 0)
|
|
152
|
+
return Bytes(data.begin() + std::ptrdiff_t(i + 4), data.end());
|
|
153
|
+
}
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Wait for a UDP datagram for up to @p total_timeout_ms, but in short slices so a
|
|
158
|
+
// pending cancellation (e.g. shutdown) aborts the wait within one slice instead
|
|
159
|
+
// of blocking for the whole timeout against an unresponsive tracker. Returns the
|
|
160
|
+
// datagram, or empty on timeout / cancellation.
|
|
161
|
+
Bytes receive_udp_sliced(socket_t sock, std::size_t maxlen, Address& sender, int total_timeout_ms,
|
|
162
|
+
const TrackerCancelPredicate& cancelled) {
|
|
163
|
+
if (!cancelled) {
|
|
164
|
+
// No cancellation wanted — one plain blocking wait (fast path).
|
|
165
|
+
return receive_udp_data(sock, maxlen, sender, total_timeout_ms);
|
|
166
|
+
}
|
|
167
|
+
constexpr int kSliceMs = 200;
|
|
168
|
+
int elapsed = 0;
|
|
169
|
+
while (elapsed < total_timeout_ms) {
|
|
170
|
+
if (cancelled()) return {};
|
|
171
|
+
const int slice = (std::min)(kSliceMs, total_timeout_ms - elapsed);
|
|
172
|
+
Bytes data = receive_udp_data(sock, maxlen, sender, slice);
|
|
173
|
+
if (!data.empty()) return data;
|
|
174
|
+
elapsed += slice;
|
|
175
|
+
}
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
TrackerResponse announce_http(const std::string& url, const TrackerRequest& req, int timeout_ms,
|
|
180
|
+
const TrackerCancelPredicate& cancelled) {
|
|
181
|
+
TrackerResponse out;
|
|
182
|
+
if (cancelled && cancelled()) { out.failure_reason = "cancelled"; return out; }
|
|
183
|
+
const Bytes body = http_get(tracker_detail::build_http_announce_url(url, req), timeout_ms);
|
|
184
|
+
if (body.empty()) { out.failure_reason = "no response"; return out; }
|
|
185
|
+
return tracker_detail::parse_http_response(body);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
TrackerResponse announce_udp(const std::string& url, const TrackerRequest& req, int timeout_ms,
|
|
189
|
+
const TrackerCancelPredicate& cancelled) {
|
|
190
|
+
TrackerResponse out;
|
|
191
|
+
if (cancelled && cancelled()) { out.failure_reason = "cancelled"; return out; }
|
|
192
|
+
|
|
193
|
+
// Parse udp://host:port[/...]
|
|
194
|
+
std::string rest = url.substr(6);
|
|
195
|
+
if (const std::size_t slash = rest.find('/'); slash != std::string::npos) rest = rest.substr(0, slash);
|
|
196
|
+
const std::size_t colon = rest.find(':');
|
|
197
|
+
if (colon == std::string::npos) { out.failure_reason = "bad udp url"; return out; }
|
|
198
|
+
const std::string host = rest.substr(0, colon);
|
|
199
|
+
const std::uint16_t port = parse_port(rest.substr(colon + 1));
|
|
200
|
+
if (port == 0) { out.failure_reason = "bad udp port"; return out; }
|
|
201
|
+
|
|
202
|
+
socket_t sock = create_udp_socket(0, "", AddressFamily::IPv4);
|
|
203
|
+
if (!is_valid_socket(sock)) { out.failure_reason = "socket"; return out; }
|
|
204
|
+
struct Closer { socket_t s; ~Closer() { if (is_valid_socket(s)) close_socket(s); } } closer{sock};
|
|
205
|
+
|
|
206
|
+
// 1) connect
|
|
207
|
+
const std::uint32_t tid1 = random_u32();
|
|
208
|
+
Bytes creq(16);
|
|
209
|
+
write_u64_be(creq.data(), std::uint64_t(kUdpMagic));
|
|
210
|
+
write_u32_be(creq.data() + 8, kActionConnect);
|
|
211
|
+
write_u32_be(creq.data() + 12, tid1);
|
|
212
|
+
if (send_udp_data(sock, creq, host, port, AddressFamily::IPv4) <= 0) { out.failure_reason = "send"; return out; }
|
|
213
|
+
|
|
214
|
+
Address sender;
|
|
215
|
+
Bytes cresp = receive_udp_sliced(sock, 2048, sender, timeout_ms, cancelled);
|
|
216
|
+
if (cresp.size() < 16 || read_u32_be(cresp.data()) != kActionConnect ||
|
|
217
|
+
read_u32_be(cresp.data() + 4) != tid1) {
|
|
218
|
+
out.failure_reason = (cancelled && cancelled()) ? "cancelled" : "connect failed";
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
const std::uint64_t conn_id = read_u64_be(cresp.data() + 8);
|
|
222
|
+
|
|
223
|
+
// 2) announce
|
|
224
|
+
const std::uint32_t tid2 = random_u32();
|
|
225
|
+
Bytes areq(98);
|
|
226
|
+
write_u64_be(areq.data(), conn_id);
|
|
227
|
+
write_u32_be(areq.data() + 8, kActionAnnounce);
|
|
228
|
+
write_u32_be(areq.data() + 12, tid2);
|
|
229
|
+
std::memcpy(areq.data() + 16, req.info_hash.data(), 20);
|
|
230
|
+
std::memcpy(areq.data() + 36, req.peer_id.data(), 20);
|
|
231
|
+
write_u64_be(areq.data() + 56, req.downloaded);
|
|
232
|
+
write_u64_be(areq.data() + 64, req.left);
|
|
233
|
+
write_u64_be(areq.data() + 72, req.uploaded);
|
|
234
|
+
write_u32_be(areq.data() + 80, udp_event(req.event));
|
|
235
|
+
write_u32_be(areq.data() + 84, 0); // our IP — let the tracker use the source
|
|
236
|
+
write_u32_be(areq.data() + 88, random_u32()); // key
|
|
237
|
+
write_u32_be(areq.data() + 92, std::uint32_t(req.numwant));
|
|
238
|
+
write_u16_be(areq.data() + 96, req.port);
|
|
239
|
+
if (send_udp_data(sock, areq, host, port, AddressFamily::IPv4) <= 0) { out.failure_reason = "send"; return out; }
|
|
240
|
+
|
|
241
|
+
Bytes resp = receive_udp_sliced(sock, 2048, sender, timeout_ms, cancelled);
|
|
242
|
+
if (resp.size() < 8) {
|
|
243
|
+
out.failure_reason = (cancelled && cancelled()) ? "cancelled" : "no response";
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
const std::uint32_t action = read_u32_be(resp.data());
|
|
247
|
+
if (action == kActionError) {
|
|
248
|
+
out.failure_reason.assign(resp.begin() + 8, resp.end());
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
if (action != kActionAnnounce || read_u32_be(resp.data() + 4) != tid2 || resp.size() < 20) {
|
|
252
|
+
out.failure_reason = "bad announce response";
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
out.interval = read_u32_be(resp.data() + 8);
|
|
257
|
+
out.incomplete = read_u32_be(resp.data() + 12);
|
|
258
|
+
out.complete = read_u32_be(resp.data() + 16);
|
|
259
|
+
for (std::size_t off = 20; off + 6 <= resp.size(); off += 6) {
|
|
260
|
+
const std::uint8_t* b = resp.data() + off;
|
|
261
|
+
std::ostringstream ip;
|
|
262
|
+
ip << int(b[0]) << '.' << int(b[1]) << '.' << int(b[2]) << '.' << int(b[3]);
|
|
263
|
+
out.peers.emplace_back(ip.str(), read_u16_be(b + 4));
|
|
264
|
+
}
|
|
265
|
+
out.success = true;
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
} // namespace
|
|
270
|
+
|
|
271
|
+
namespace tracker_detail {
|
|
272
|
+
|
|
273
|
+
std::string build_http_announce_url(const std::string& base, const TrackerRequest& req) {
|
|
274
|
+
std::ostringstream u;
|
|
275
|
+
u << base << (base.find('?') == std::string::npos ? '?' : '&');
|
|
276
|
+
u << "info_hash=" << url_encode_binary(req.info_hash.data(), req.info_hash.size());
|
|
277
|
+
u << "&peer_id=" << url_encode_binary(req.peer_id.data(), req.peer_id.size());
|
|
278
|
+
u << "&port=" << req.port;
|
|
279
|
+
u << "&uploaded=" << req.uploaded;
|
|
280
|
+
u << "&downloaded=" << req.downloaded;
|
|
281
|
+
u << "&left=" << req.left;
|
|
282
|
+
u << "&numwant=" << req.numwant;
|
|
283
|
+
u << "&compact=1";
|
|
284
|
+
if (req.event != TrackerEvent::None) u << "&event=" << http_event(req.event);
|
|
285
|
+
return u.str();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
TrackerResponse parse_http_response(const Bytes& body) {
|
|
289
|
+
TrackerResponse out;
|
|
290
|
+
try {
|
|
291
|
+
librats::BencodeValue d = librats::BencodeDecoder::decode(body.data(), body.size());
|
|
292
|
+
if (!d.is_dict()) { out.failure_reason = "not a dict"; return out; }
|
|
293
|
+
if (d.has_key("failure reason")) { out.failure_reason = d["failure reason"].as_string(); return out; }
|
|
294
|
+
if (d.has_key("interval")) out.interval = std::uint32_t(d["interval"].as_integer());
|
|
295
|
+
if (d.has_key("min interval")) out.min_interval = std::uint32_t(d["min interval"].as_integer());
|
|
296
|
+
if (d.has_key("complete")) out.complete = std::uint32_t(d["complete"].as_integer());
|
|
297
|
+
if (d.has_key("incomplete")) out.incomplete = std::uint32_t(d["incomplete"].as_integer());
|
|
298
|
+
if (d.has_key("peers")) {
|
|
299
|
+
const auto& p = d["peers"];
|
|
300
|
+
if (p.is_string()) out.peers = parse_compact_peers(p.as_string());
|
|
301
|
+
else if (p.is_list()) out.peers = parse_dict_peers(p);
|
|
302
|
+
}
|
|
303
|
+
out.success = true;
|
|
304
|
+
} catch (const std::exception& e) {
|
|
305
|
+
out.failure_reason = e.what();
|
|
306
|
+
}
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
} // namespace tracker_detail
|
|
311
|
+
|
|
312
|
+
TrackerResponse announce_to_tracker(const std::string& url, const TrackerRequest& req, int timeout_ms,
|
|
313
|
+
const TrackerCancelPredicate& cancelled) {
|
|
314
|
+
if (url.rfind("udp://", 0) == 0) return announce_udp(url, req, timeout_ms, cancelled);
|
|
315
|
+
// We have no TLS for trackers yet. Skip https:// rather than speaking cleartext
|
|
316
|
+
// HTTP to port 443 — that never works and would leak info_hash/peer_id in the
|
|
317
|
+
// clear (H11). Re-enable once a real TLS stream is wired in.
|
|
318
|
+
if (url.rfind("https://", 0) == 0) {
|
|
319
|
+
TrackerResponse out; out.failure_reason = "https trackers not supported (no TLS)";
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
if (url.rfind("http://", 0) == 0) return announce_http(url, req, timeout_ms, cancelled);
|
|
323
|
+
TrackerResponse out;
|
|
324
|
+
out.failure_reason = "unsupported tracker scheme";
|
|
325
|
+
return out;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ---- TrackerAnnouncer ----
|
|
329
|
+
|
|
330
|
+
TrackerAnnouncer::TrackerAnnouncer(std::vector<std::string> trackers, Poster poster, int timeout_ms)
|
|
331
|
+
: trackers_(std::move(trackers)), poster_(std::move(poster)), timeout_ms_(timeout_ms) {}
|
|
332
|
+
|
|
333
|
+
TrackerAnnouncer::~TrackerAnnouncer() { stop(); }
|
|
334
|
+
|
|
335
|
+
void TrackerAnnouncer::announce(const TrackerRequest& req, PeerCallback on_peers) {
|
|
336
|
+
for (const std::string& url : trackers_) {
|
|
337
|
+
{
|
|
338
|
+
std::lock_guard<std::mutex> lk(mutex_);
|
|
339
|
+
if (stopping_) return;
|
|
340
|
+
++inflight_;
|
|
341
|
+
}
|
|
342
|
+
std::thread([this, url, req, on_peers] {
|
|
343
|
+
TrackerResponse resp = announce_to_tracker(url, req, timeout_ms_);
|
|
344
|
+
if (resp.success)
|
|
345
|
+
LOG_DEBUG("bt.tracker", "← " << url << ": " << resp.peers.size() << " peers, interval "
|
|
346
|
+
<< resp.interval << "s");
|
|
347
|
+
else
|
|
348
|
+
LOG_DEBUG("bt.tracker", "✗ " << url << ": " << resp.failure_reason);
|
|
349
|
+
{
|
|
350
|
+
std::lock_guard<std::mutex> lk(mutex_);
|
|
351
|
+
if (!stopping_ && resp.success && poster_) {
|
|
352
|
+
// Honour the tracker's requested re-announce cadence (clamped to a
|
|
353
|
+
// sane 1 min..1 h) so we don't get rate-limited/banned (H13).
|
|
354
|
+
std::uint32_t interval = (std::max)(resp.interval, resp.min_interval);
|
|
355
|
+
if (interval == 0) interval = 1800;
|
|
356
|
+
interval = std::min<std::uint32_t>(std::max<std::uint32_t>(interval, 60), 3600);
|
|
357
|
+
poster_([on_peers, peers = resp.peers, interval] { on_peers(peers, interval); });
|
|
358
|
+
}
|
|
359
|
+
--inflight_;
|
|
360
|
+
// Notify while still holding the lock: once inflight_ hits zero a waiting
|
|
361
|
+
// stop() may return and the announcer be destroyed, taking drain_cv_ with it.
|
|
362
|
+
drain_cv_.notify_all();
|
|
363
|
+
}
|
|
364
|
+
}).detach();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
void TrackerAnnouncer::stop() {
|
|
369
|
+
std::unique_lock<std::mutex> lk(mutex_);
|
|
370
|
+
stopping_ = true;
|
|
371
|
+
drain_cv_.wait(lk, [this] { return inflight_ == 0; });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
} // namespace librats::bittorrent
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file tracker.h
|
|
5
|
+
* @brief Tracker announces (BEP 3 over HTTP, BEP 15 over UDP) and the per-torrent
|
|
6
|
+
* announcer that drives them.
|
|
7
|
+
*
|
|
8
|
+
* A tracker is a directory: you periodically POST your progress and it hands back
|
|
9
|
+
* a list of peers. The wire work is blocking (a short HTTP request or a two-step
|
|
10
|
+
* UDP connect→announce), so announce_to_tracker() is a self-contained blocking
|
|
11
|
+
* call with no shared state — easy to test and safe to run off-thread.
|
|
12
|
+
*
|
|
13
|
+
* TrackerAnnouncer is what a Torrent owns: it fires announces to every tracker on
|
|
14
|
+
* background threads and delivers discovered peers back through a poster (onto the
|
|
15
|
+
* reactor), exactly like the disk subsystem. stop() joins any in-flight announce
|
|
16
|
+
* so a torrent never tears down underneath one.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#include "librats/bittorrent/types.h"
|
|
20
|
+
#include "librats/core/address.h"
|
|
21
|
+
#include "librats/core/bytes.h"
|
|
22
|
+
|
|
23
|
+
#include <condition_variable>
|
|
24
|
+
#include <cstdint>
|
|
25
|
+
#include <functional>
|
|
26
|
+
#include <mutex>
|
|
27
|
+
#include <string>
|
|
28
|
+
#include <vector>
|
|
29
|
+
|
|
30
|
+
namespace librats::bittorrent {
|
|
31
|
+
|
|
32
|
+
enum class TrackerEvent { None, Started, Stopped, Completed };
|
|
33
|
+
|
|
34
|
+
struct TrackerRequest {
|
|
35
|
+
InfoHash info_hash{};
|
|
36
|
+
PeerId peer_id{};
|
|
37
|
+
std::uint16_t port = 0;
|
|
38
|
+
std::uint64_t uploaded = 0;
|
|
39
|
+
std::uint64_t downloaded = 0;
|
|
40
|
+
std::uint64_t left = 0;
|
|
41
|
+
TrackerEvent event = TrackerEvent::None;
|
|
42
|
+
int numwant = 50;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
struct TrackerResponse {
|
|
46
|
+
bool success = false;
|
|
47
|
+
std::string failure_reason;
|
|
48
|
+
std::uint32_t interval = 1800; ///< seconds until the next announce
|
|
49
|
+
std::uint32_t min_interval = 0;
|
|
50
|
+
std::uint32_t complete = 0; ///< seeders
|
|
51
|
+
std::uint32_t incomplete = 0; ///< leechers
|
|
52
|
+
std::vector<Address> peers;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/// Polled by the blocking announce so a caller can abort it early (e.g. on
|
|
56
|
+
/// shutdown): when it returns true the announce bails within ~one poll slice
|
|
57
|
+
/// instead of blocking for the full timeout. An empty predicate never cancels.
|
|
58
|
+
using TrackerCancelPredicate = std::function<bool()>;
|
|
59
|
+
|
|
60
|
+
/// Perform one blocking announce to @p url (http/https/udp). Self-contained: it
|
|
61
|
+
/// opens, uses and closes its own socket and shares no state with anything.
|
|
62
|
+
/// @p cancelled, if set, is polled during the wait so the call can be aborted
|
|
63
|
+
/// promptly (the blocking receive is done in short slices between polls).
|
|
64
|
+
TrackerResponse announce_to_tracker(const std::string& url, const TrackerRequest& req,
|
|
65
|
+
int timeout_ms = 10000,
|
|
66
|
+
const TrackerCancelPredicate& cancelled = {});
|
|
67
|
+
|
|
68
|
+
/// Drives announces to a torrent's trackers from background threads, delivering
|
|
69
|
+
/// peers via a poster (the reactor). Owned by one Torrent.
|
|
70
|
+
class TrackerAnnouncer {
|
|
71
|
+
public:
|
|
72
|
+
using Poster = std::function<void(std::function<void()>)>;
|
|
73
|
+
/// Delivers a tracker's response: discovered peers and the interval (seconds)
|
|
74
|
+
/// the tracker wants before the next announce (already max'd with min-interval).
|
|
75
|
+
using PeerCallback = std::function<void(const std::vector<Address>& peers, std::uint32_t interval)>;
|
|
76
|
+
|
|
77
|
+
TrackerAnnouncer(std::vector<std::string> trackers, Poster poster, int timeout_ms = 10000);
|
|
78
|
+
~TrackerAnnouncer();
|
|
79
|
+
|
|
80
|
+
TrackerAnnouncer(const TrackerAnnouncer&) = delete;
|
|
81
|
+
TrackerAnnouncer& operator=(const TrackerAnnouncer&) = delete;
|
|
82
|
+
|
|
83
|
+
/// Announce to every tracker (each on its own worker thread); @p on_peers is
|
|
84
|
+
/// posted onto the reactor for each tracker that returns peers.
|
|
85
|
+
void announce(const TrackerRequest& req, PeerCallback on_peers);
|
|
86
|
+
/// Wait for any in-flight announce to finish. Idempotent; called by the dtor.
|
|
87
|
+
void stop();
|
|
88
|
+
|
|
89
|
+
std::size_t tracker_count() const noexcept { return trackers_.size(); }
|
|
90
|
+
|
|
91
|
+
private:
|
|
92
|
+
std::vector<std::string> trackers_;
|
|
93
|
+
Poster poster_;
|
|
94
|
+
int timeout_ms_;
|
|
95
|
+
|
|
96
|
+
std::mutex mutex_;
|
|
97
|
+
std::condition_variable drain_cv_;
|
|
98
|
+
int inflight_ = 0;
|
|
99
|
+
bool stopping_ = false;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Exposed for unit testing the HTTP wire format without a live tracker.
|
|
103
|
+
namespace tracker_detail {
|
|
104
|
+
std::string build_http_announce_url(const std::string& base, const TrackerRequest& req);
|
|
105
|
+
TrackerResponse parse_http_response(const Bytes& body);
|
|
106
|
+
} // namespace tracker_detail
|
|
107
|
+
|
|
108
|
+
} // namespace librats::bittorrent
|