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,314 @@
|
|
|
1
|
+
#include "librats/bittorrent/torrent_info.h"
|
|
2
|
+
|
|
3
|
+
#include "librats/bittorrent/bencode.h"
|
|
4
|
+
#include "librats/bittorrent/magnet_uri.h"
|
|
5
|
+
#include "librats/crypto/sha1.h"
|
|
6
|
+
#include "librats/util/fs.h"
|
|
7
|
+
|
|
8
|
+
#include <cctype>
|
|
9
|
+
#include <cstddef>
|
|
10
|
+
#include <cstring>
|
|
11
|
+
|
|
12
|
+
namespace librats::bittorrent {
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
// ----------------------------------------------------------------------------
|
|
17
|
+
// Minimal bencode byte-span scanner.
|
|
18
|
+
//
|
|
19
|
+
// We must hash the *original* bytes of the `info` value (re-encoding the parsed
|
|
20
|
+
// structure could reorder dict keys and change the hash). The eager BencodeValue
|
|
21
|
+
// decoder discards positions, so this scanner walks the raw buffer to locate the
|
|
22
|
+
// info value's byte range without building a tree.
|
|
23
|
+
// ----------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/// Read a bencoded byte string at @p pos, advancing @p pos past it.
|
|
26
|
+
bool read_bstring(const std::uint8_t* d, std::size_t n, std::size_t& pos, std::string& out) {
|
|
27
|
+
std::size_t colon = pos;
|
|
28
|
+
while (colon < n && d[colon] != ':') {
|
|
29
|
+
if (d[colon] < '0' || d[colon] > '9') return false;
|
|
30
|
+
++colon;
|
|
31
|
+
}
|
|
32
|
+
if (colon >= n || colon == pos) return false;
|
|
33
|
+
std::uint64_t len = 0;
|
|
34
|
+
for (std::size_t i = pos; i < colon; ++i) {
|
|
35
|
+
len = len * 10 + std::uint64_t(d[i] - '0');
|
|
36
|
+
if (len > n) return false; // can't be longer than the whole buffer
|
|
37
|
+
}
|
|
38
|
+
const std::size_t start = colon + 1;
|
|
39
|
+
if (start + len > n) return false;
|
|
40
|
+
out.assign(reinterpret_cast<const char*>(d + start), len);
|
|
41
|
+
pos = start + len;
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Advance @p pos past exactly one bencoded value (int / string / list / dict).
|
|
46
|
+
bool skip_value(const std::uint8_t* d, std::size_t n, std::size_t& pos) {
|
|
47
|
+
if (pos >= n) return false;
|
|
48
|
+
const char c = char(d[pos]);
|
|
49
|
+
if (c == 'i') {
|
|
50
|
+
std::size_t e = pos + 1;
|
|
51
|
+
while (e < n && d[e] != 'e') ++e;
|
|
52
|
+
if (e >= n) return false;
|
|
53
|
+
pos = e + 1;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (c == 'l' || c == 'd') {
|
|
57
|
+
++pos;
|
|
58
|
+
while (pos < n && d[pos] != 'e') {
|
|
59
|
+
if (!skip_value(d, n, pos)) return false;
|
|
60
|
+
}
|
|
61
|
+
if (pos >= n) return false;
|
|
62
|
+
++pos; // consume 'e'
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (c >= '0' && c <= '9') {
|
|
66
|
+
std::string tmp;
|
|
67
|
+
return read_bstring(d, n, pos, tmp);
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Locate the byte range [start, end) of the top-level dict's `info` value.
|
|
73
|
+
std::optional<std::pair<std::size_t, std::size_t>> find_info_span(const Bytes& buf) {
|
|
74
|
+
const std::uint8_t* d = buf.data();
|
|
75
|
+
const std::size_t n = buf.size();
|
|
76
|
+
if (n == 0 || d[0] != 'd') return std::nullopt;
|
|
77
|
+
|
|
78
|
+
std::size_t pos = 1;
|
|
79
|
+
while (pos < n && d[pos] != 'e') {
|
|
80
|
+
std::string key;
|
|
81
|
+
if (!read_bstring(d, n, pos, key)) return std::nullopt;
|
|
82
|
+
const std::size_t value_start = pos;
|
|
83
|
+
if (!skip_value(d, n, pos)) return std::nullopt;
|
|
84
|
+
if (key == "info") return std::make_pair(value_start, pos);
|
|
85
|
+
}
|
|
86
|
+
return std::nullopt;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---- safe bencode accessors (the BencodeValue accessors throw on mismatch) ----
|
|
90
|
+
|
|
91
|
+
const librats::BencodeValue* find(const librats::BencodeValue& dict, const char* key) {
|
|
92
|
+
return dict.find(key);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const std::string* find_string(const librats::BencodeValue& dict, const char* key) {
|
|
96
|
+
const auto* v = find(dict, key);
|
|
97
|
+
return (v && v->is_string()) ? &v->as_string() : nullptr;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
std::optional<std::int64_t> find_int(const librats::BencodeValue& dict, const char* key) {
|
|
101
|
+
const auto* v = find(dict, key);
|
|
102
|
+
if (v && v->is_integer()) return v->as_integer();
|
|
103
|
+
return std::nullopt;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// A path component is safe if it is non-empty, not "." / ".." and free of
|
|
107
|
+
/// separators / NULs — guarding the disk layer against traversal.
|
|
108
|
+
bool safe_component(const std::string& c) {
|
|
109
|
+
if (c.empty() || c == "." || c == "..") return false;
|
|
110
|
+
return c.find('/') == std::string::npos && c.find('\\') == std::string::npos
|
|
111
|
+
&& c.find('\0') == std::string::npos;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
} // namespace
|
|
115
|
+
|
|
116
|
+
bool TorrentInfo::parse_info_dict(const librats::BencodeValue& info, TorrentParseError* err) {
|
|
117
|
+
auto fail = [&](const char* m) { if (err) err->message = m; return false; };
|
|
118
|
+
|
|
119
|
+
const std::string* name = find_string(info, "name");
|
|
120
|
+
const auto piece_length = find_int(info, "piece length");
|
|
121
|
+
const std::string* pieces = find_string(info, "pieces");
|
|
122
|
+
if (!name || !piece_length || !pieces) return fail("info dict missing name/piece length/pieces");
|
|
123
|
+
if (*piece_length <= 0 || *piece_length > (1 << 30)) return fail("invalid piece length");
|
|
124
|
+
if (pieces->size() % kInfoHashSize != 0) return fail("pieces string not a multiple of 20");
|
|
125
|
+
if (!safe_component(*name)) return fail("unsafe torrent name");
|
|
126
|
+
|
|
127
|
+
FileStorage files;
|
|
128
|
+
files.set_piece_length(std::uint32_t(*piece_length));
|
|
129
|
+
files.set_name(*name);
|
|
130
|
+
|
|
131
|
+
if (const auto* file_list = find(info, "files"); file_list && file_list->is_list()) {
|
|
132
|
+
// Multi-file: each entry has a length and a path component list.
|
|
133
|
+
for (const auto& entry : file_list->as_list()) {
|
|
134
|
+
const auto length = find_int(entry, "length");
|
|
135
|
+
const auto* path = find(entry, "path");
|
|
136
|
+
if (!length || *length < 0 || !path || !path->is_list()) return fail("bad file entry");
|
|
137
|
+
std::string rel = *name;
|
|
138
|
+
for (const auto& comp : path->as_list()) {
|
|
139
|
+
if (!comp.is_string() || !safe_component(comp.as_string())) return fail("unsafe file path");
|
|
140
|
+
rel += '/';
|
|
141
|
+
rel += comp.as_string();
|
|
142
|
+
}
|
|
143
|
+
if (!files.add_file(std::move(rel), *length)) return fail("file size overflow");
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// Single-file: the info dict itself carries the length.
|
|
147
|
+
const auto length = find_int(info, "length");
|
|
148
|
+
if (!length || *length < 0) return fail("single-file torrent missing length");
|
|
149
|
+
if (!files.add_file(*name, *length)) return fail("file size overflow");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The piece-hash count must match the file layout exactly.
|
|
153
|
+
if (pieces->size() / kInfoHashSize != files.num_pieces()) return fail("piece count mismatch");
|
|
154
|
+
|
|
155
|
+
files_ = std::move(files);
|
|
156
|
+
name_ = *name;
|
|
157
|
+
piece_hashes_.assign(pieces->begin(), pieces->end());
|
|
158
|
+
is_private_ = find_int(info, "private").value_or(0) == 1;
|
|
159
|
+
has_metadata_ = true;
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
std::optional<TorrentInfo> TorrentInfo::from_info_dict(const Bytes& info_dict_bytes,
|
|
164
|
+
const InfoHash& expected,
|
|
165
|
+
TorrentParseError* err) {
|
|
166
|
+
try {
|
|
167
|
+
librats::BencodeValue info = librats::BencodeDecoder::decode(info_dict_bytes);
|
|
168
|
+
TorrentInfo ti;
|
|
169
|
+
if (!ti.parse_info_dict(info, err)) return std::nullopt;
|
|
170
|
+
ti.info_dict_bytes_ = info_dict_bytes;
|
|
171
|
+
ti.info_hash_ = SHA1::hash_raw(info_dict_bytes.data(), info_dict_bytes.size());
|
|
172
|
+
if (!is_all_zero(expected) && ti.info_hash_ != expected) {
|
|
173
|
+
if (err) err->message = "info-hash mismatch";
|
|
174
|
+
return std::nullopt;
|
|
175
|
+
}
|
|
176
|
+
return ti;
|
|
177
|
+
} catch (const std::exception& e) {
|
|
178
|
+
if (err) err->message = e.what();
|
|
179
|
+
return std::nullopt;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
std::optional<TorrentInfo> TorrentInfo::from_bytes(const Bytes& data, TorrentParseError* err) {
|
|
184
|
+
try {
|
|
185
|
+
librats::BencodeValue root = librats::BencodeDecoder::decode(data);
|
|
186
|
+
if (!root.is_dict()) { if (err) err->message = "torrent is not a dict"; return std::nullopt; }
|
|
187
|
+
|
|
188
|
+
const auto span = find_info_span(data);
|
|
189
|
+
const auto* info = find(root, "info");
|
|
190
|
+
if (!span || !info) { if (err) err->message = "missing info dict"; return std::nullopt; }
|
|
191
|
+
|
|
192
|
+
TorrentInfo ti;
|
|
193
|
+
if (!ti.parse_info_dict(*info, err)) return std::nullopt;
|
|
194
|
+
|
|
195
|
+
ti.info_dict_bytes_.assign(data.begin() + std::ptrdiff_t(span->first),
|
|
196
|
+
data.begin() + std::ptrdiff_t(span->second));
|
|
197
|
+
ti.info_hash_ = SHA1::hash_raw(ti.info_dict_bytes_.data(), ti.info_dict_bytes_.size());
|
|
198
|
+
|
|
199
|
+
// Optional top-level fields.
|
|
200
|
+
if (const auto* a = find_string(root, "announce")) ti.announce_ = *a;
|
|
201
|
+
if (const auto* c = find_string(root, "comment")) ti.comment_ = *c;
|
|
202
|
+
if (const auto* cb = find_string(root, "created by")) ti.created_by_ = *cb;
|
|
203
|
+
if (auto cd = find_int(root, "creation date")) ti.creation_date_ = *cd;
|
|
204
|
+
|
|
205
|
+
if (const auto* al = find(root, "announce-list"); al && al->is_list()) {
|
|
206
|
+
for (const auto& tier : al->as_list()) {
|
|
207
|
+
if (!tier.is_list()) continue;
|
|
208
|
+
TrackerTier t;
|
|
209
|
+
for (const auto& url : tier.as_list())
|
|
210
|
+
if (url.is_string()) t.push_back(url.as_string());
|
|
211
|
+
if (!t.empty()) ti.announce_list_.push_back(std::move(t));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (const auto* ul = find(root, "url-list")) { // web seeds (BEP 19)
|
|
216
|
+
if (ul->is_string()) ti.web_seeds_.push_back(ul->as_string());
|
|
217
|
+
else if (ul->is_list())
|
|
218
|
+
for (const auto& u : ul->as_list())
|
|
219
|
+
if (u.is_string()) ti.web_seeds_.push_back(u.as_string());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (const auto* nodes = find(root, "nodes"); nodes && nodes->is_list()) {
|
|
223
|
+
for (const auto& node : nodes->as_list()) {
|
|
224
|
+
if (!node.is_list() || node.as_list().size() != 2) continue;
|
|
225
|
+
const auto& pair = node.as_list();
|
|
226
|
+
if (pair[0].is_string() && pair[1].is_integer())
|
|
227
|
+
ti.dht_nodes_.push_back(DhtNode{pair[0].as_string(),
|
|
228
|
+
std::uint16_t(pair[1].as_integer())});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return ti;
|
|
233
|
+
} catch (const std::exception& e) {
|
|
234
|
+
if (err) err->message = e.what();
|
|
235
|
+
return std::nullopt;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
std::optional<TorrentInfo> TorrentInfo::from_file(const std::string& path, TorrentParseError* err) {
|
|
240
|
+
std::size_t size = 0;
|
|
241
|
+
void* buf = read_file_binary(path.c_str(), &size);
|
|
242
|
+
if (!buf) { if (err) err->message = "cannot read file"; return std::nullopt; }
|
|
243
|
+
Bytes data(static_cast<std::uint8_t*>(buf), static_cast<std::uint8_t*>(buf) + size);
|
|
244
|
+
free_file_buffer(buf);
|
|
245
|
+
return from_bytes(data, err);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
std::optional<TorrentInfo> TorrentInfo::from_magnet(const std::string& uri, TorrentParseError* err) {
|
|
249
|
+
auto magnet = MagnetUri::parse(uri);
|
|
250
|
+
if (!magnet) { if (err) err->message = "invalid magnet uri"; return std::nullopt; }
|
|
251
|
+
|
|
252
|
+
TorrentInfo ti;
|
|
253
|
+
ti.info_hash_ = magnet->info_hash;
|
|
254
|
+
ti.name_ = magnet->display_name;
|
|
255
|
+
ti.web_seeds_ = magnet->web_seeds;
|
|
256
|
+
ti.has_metadata_ = false; // file list & piece hashes still need fetching
|
|
257
|
+
for (const auto& tr : magnet->trackers) ti.announce_list_.push_back({tr});
|
|
258
|
+
if (!ti.announce_list_.empty() && !ti.announce_list_.front().empty())
|
|
259
|
+
ti.announce_ = ti.announce_list_.front().front();
|
|
260
|
+
return ti;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
bool TorrentInfo::set_metadata(const Bytes& info_dict_bytes) {
|
|
264
|
+
auto completed = from_info_dict(info_dict_bytes, info_hash_, nullptr);
|
|
265
|
+
if (!completed) return false;
|
|
266
|
+
// Preserve discovery fields learned from the magnet; adopt the parsed payload.
|
|
267
|
+
files_ = std::move(completed->files_);
|
|
268
|
+
piece_hashes_ = std::move(completed->piece_hashes_);
|
|
269
|
+
info_dict_bytes_ = std::move(completed->info_dict_bytes_);
|
|
270
|
+
is_private_ = completed->is_private_;
|
|
271
|
+
if (name_.empty()) name_ = completed->name_;
|
|
272
|
+
has_metadata_ = true;
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
std::array<std::uint8_t, 20> TorrentInfo::piece_hash(std::uint32_t index) const {
|
|
277
|
+
std::array<std::uint8_t, 20> out{};
|
|
278
|
+
const std::size_t off = std::size_t(index) * kInfoHashSize;
|
|
279
|
+
if (off + kInfoHashSize <= piece_hashes_.size())
|
|
280
|
+
std::memcpy(out.data(), piece_hashes_.data() + off, kInfoHashSize);
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
std::vector<std::string> TorrentInfo::all_trackers() const {
|
|
285
|
+
std::vector<std::string> all;
|
|
286
|
+
if (!announce_.empty()) all.push_back(announce_);
|
|
287
|
+
for (const auto& tier : announce_list_)
|
|
288
|
+
for (const auto& url : tier)
|
|
289
|
+
if (url != announce_) all.push_back(url);
|
|
290
|
+
return all;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
std::string TorrentInfo::to_magnet_uri(bool include_trackers) const {
|
|
294
|
+
std::string uri = "magnet:?xt=urn:btih:" + to_hex(info_hash_);
|
|
295
|
+
if (!name_.empty()) {
|
|
296
|
+
uri += "&dn=";
|
|
297
|
+
// Percent-encode anything that isn't an unreserved URI character.
|
|
298
|
+
for (unsigned char c : name_) {
|
|
299
|
+
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
300
|
+
uri += char(c);
|
|
301
|
+
} else {
|
|
302
|
+
static const char hex[] = "0123456789ABCDEF";
|
|
303
|
+
uri += '%';
|
|
304
|
+
uri += hex[c >> 4];
|
|
305
|
+
uri += hex[c & 0x0F];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (include_trackers)
|
|
310
|
+
for (const auto& tr : all_trackers()) uri += "&tr=" + tr;
|
|
311
|
+
return uri;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
} // namespace librats::bittorrent
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file torrent_info.h
|
|
5
|
+
* @brief Parsed torrent metadata: file layout, piece hashes, trackers, info-hash.
|
|
6
|
+
*
|
|
7
|
+
* A TorrentInfo is built from a .torrent file, a bare info dictionary (received
|
|
8
|
+
* via ut_metadata) or a magnet link. From a magnet it is "valid but without
|
|
9
|
+
* metadata" — it knows only the info-hash until set_metadata() completes it.
|
|
10
|
+
*
|
|
11
|
+
* The info-hash is the SHA-1 of the *exact* bytes of the bencoded `info`
|
|
12
|
+
* dictionary. We therefore keep those bytes verbatim (info_dict_bytes_) rather
|
|
13
|
+
* than re-encoding the parsed structure, so re-serving them over BEP 9 and
|
|
14
|
+
* re-hashing always reproduce the original hash.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "librats/util/rats_export.h"
|
|
18
|
+
#include "librats/bittorrent/file_storage.h"
|
|
19
|
+
#include "librats/bittorrent/types.h"
|
|
20
|
+
#include "librats/core/bytes.h"
|
|
21
|
+
|
|
22
|
+
#include <array>
|
|
23
|
+
#include <cstdint>
|
|
24
|
+
#include <optional>
|
|
25
|
+
#include <string>
|
|
26
|
+
#include <vector>
|
|
27
|
+
|
|
28
|
+
namespace librats { class BencodeValue; }
|
|
29
|
+
|
|
30
|
+
namespace librats::bittorrent {
|
|
31
|
+
|
|
32
|
+
/// Diagnostic for a parse failure (optional out-param of the factories).
|
|
33
|
+
struct TorrentParseError {
|
|
34
|
+
std::string message;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
class RATS_API TorrentInfo {
|
|
38
|
+
public:
|
|
39
|
+
using TrackerTier = std::vector<std::string>;
|
|
40
|
+
struct DhtNode { std::string host; std::uint16_t port = 0; };
|
|
41
|
+
|
|
42
|
+
TorrentInfo() = default;
|
|
43
|
+
|
|
44
|
+
// ---- Factories ----
|
|
45
|
+
static std::optional<TorrentInfo> from_bytes(const Bytes& data, TorrentParseError* err = nullptr);
|
|
46
|
+
static std::optional<TorrentInfo> from_file(const std::string& path, TorrentParseError* err = nullptr);
|
|
47
|
+
static std::optional<TorrentInfo> from_magnet(const std::string& uri, TorrentParseError* err = nullptr);
|
|
48
|
+
/// Build directly from a bencoded info dict. If @p expected is non-zero, the
|
|
49
|
+
/// computed info-hash must match it.
|
|
50
|
+
static std::optional<TorrentInfo> from_info_dict(const Bytes& info_dict_bytes,
|
|
51
|
+
const InfoHash& expected,
|
|
52
|
+
TorrentParseError* err = nullptr);
|
|
53
|
+
|
|
54
|
+
/// Complete a magnet-built TorrentInfo with the info dict fetched from peers.
|
|
55
|
+
/// Returns false (leaving the object unchanged) if the bytes don't hash to
|
|
56
|
+
/// the expected info-hash or fail to parse.
|
|
57
|
+
bool set_metadata(const Bytes& info_dict_bytes);
|
|
58
|
+
|
|
59
|
+
// ---- Core properties ----
|
|
60
|
+
bool is_valid() const noexcept { return !is_all_zero(info_hash_); }
|
|
61
|
+
bool has_metadata() const noexcept { return has_metadata_; }
|
|
62
|
+
|
|
63
|
+
const InfoHash& info_hash() const noexcept { return info_hash_; }
|
|
64
|
+
std::string info_hash_hex() const { return to_hex(info_hash_); }
|
|
65
|
+
const std::string& name() const noexcept { return name_; }
|
|
66
|
+
const std::string& comment() const noexcept { return comment_; }
|
|
67
|
+
const std::string& created_by() const noexcept { return created_by_; }
|
|
68
|
+
std::int64_t creation_date()const noexcept { return creation_date_; }
|
|
69
|
+
bool is_private() const noexcept { return is_private_; }
|
|
70
|
+
|
|
71
|
+
// ---- File & piece info ----
|
|
72
|
+
const FileStorage& files() const noexcept { return files_; }
|
|
73
|
+
std::int64_t total_size() const noexcept { return files_.total_size(); }
|
|
74
|
+
std::size_t num_files() const noexcept { return files_.num_files(); }
|
|
75
|
+
std::uint32_t piece_length() const noexcept { return files_.piece_length(); }
|
|
76
|
+
std::uint32_t num_pieces() const noexcept { return files_.num_pieces(); }
|
|
77
|
+
std::uint32_t piece_size(std::uint32_t i) const noexcept { return files_.piece_size(i); }
|
|
78
|
+
|
|
79
|
+
/// 20-byte SHA-1 of piece @p index (all-zero if out of range).
|
|
80
|
+
std::array<std::uint8_t, 20> piece_hash(std::uint32_t index) const;
|
|
81
|
+
const Bytes& piece_hashes() const noexcept { return piece_hashes_; }
|
|
82
|
+
|
|
83
|
+
// ---- Trackers / seeds / nodes ----
|
|
84
|
+
const std::string& announce() const noexcept { return announce_; }
|
|
85
|
+
const std::vector<TrackerTier>& announce_list() const noexcept { return announce_list_; }
|
|
86
|
+
std::vector<std::string> all_trackers() const;
|
|
87
|
+
const std::vector<std::string>& web_seeds() const noexcept { return web_seeds_; }
|
|
88
|
+
const std::vector<DhtNode>& dht_nodes() const noexcept { return dht_nodes_; }
|
|
89
|
+
|
|
90
|
+
// ---- Raw info dict (for BEP 9) ----
|
|
91
|
+
const Bytes& info_dict_bytes() const noexcept { return info_dict_bytes_; }
|
|
92
|
+
std::size_t metadata_size() const noexcept { return info_dict_bytes_.size(); }
|
|
93
|
+
|
|
94
|
+
/// Build a magnet URI for this torrent.
|
|
95
|
+
std::string to_magnet_uri(bool include_trackers = true) const;
|
|
96
|
+
|
|
97
|
+
private:
|
|
98
|
+
/// Parse name/piece-length/pieces/files/private out of a bencoded info dict
|
|
99
|
+
/// into files_ and piece_hashes_. Does not touch info_hash_/info_dict_bytes_.
|
|
100
|
+
bool parse_info_dict(const librats::BencodeValue& info, TorrentParseError* err);
|
|
101
|
+
|
|
102
|
+
InfoHash info_hash_{};
|
|
103
|
+
std::string name_;
|
|
104
|
+
std::string comment_;
|
|
105
|
+
std::string created_by_;
|
|
106
|
+
std::int64_t creation_date_ = 0;
|
|
107
|
+
bool is_private_ = false;
|
|
108
|
+
bool has_metadata_ = false;
|
|
109
|
+
FileStorage files_;
|
|
110
|
+
Bytes piece_hashes_;
|
|
111
|
+
std::string announce_;
|
|
112
|
+
std::vector<TrackerTier> announce_list_;
|
|
113
|
+
std::vector<std::string> web_seeds_;
|
|
114
|
+
std::vector<DhtNode> dht_nodes_;
|
|
115
|
+
Bytes info_dict_bytes_;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
} // namespace librats::bittorrent
|