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
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file bt_extension.h
|
|
5
|
-
* @brief BitTorrent extension protocol (BEP 10)
|
|
6
|
-
*
|
|
7
|
-
* Base interface for protocol extensions like:
|
|
8
|
-
* - ut_metadata (BEP 9): Metadata exchange for magnet links
|
|
9
|
-
* - ut_pex: Peer exchange
|
|
10
|
-
* - lt_donthave: Don't have notification
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
#include "bt_types.h"
|
|
14
|
-
#include "bencode.h"
|
|
15
|
-
|
|
16
|
-
#include <string>
|
|
17
|
-
#include <vector>
|
|
18
|
-
#include <memory>
|
|
19
|
-
#include <unordered_map>
|
|
20
|
-
#include <functional>
|
|
21
|
-
|
|
22
|
-
namespace librats {
|
|
23
|
-
|
|
24
|
-
// Forward declarations
|
|
25
|
-
class BtPeerConnection;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @brief Base class for BitTorrent extensions
|
|
29
|
-
*
|
|
30
|
-
* Extensions handle specific extension messages and can add
|
|
31
|
-
* data to the extension handshake.
|
|
32
|
-
*/
|
|
33
|
-
class BtExtension {
|
|
34
|
-
public:
|
|
35
|
-
virtual ~BtExtension() = default;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @brief Get the extension name (e.g., "ut_metadata")
|
|
39
|
-
*/
|
|
40
|
-
virtual std::string name() const = 0;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @brief Called when the peer's extension handshake is received
|
|
44
|
-
*
|
|
45
|
-
* @param handshake The peer's handshake dictionary
|
|
46
|
-
*/
|
|
47
|
-
virtual void on_handshake(const BencodeDict& handshake) = 0;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @brief Handle an extension message from the peer
|
|
51
|
-
*
|
|
52
|
-
* @param msg_id The local message ID (from our extension handshake)
|
|
53
|
-
* @param payload The message payload (after extension ID byte)
|
|
54
|
-
* @return true if message was handled
|
|
55
|
-
*/
|
|
56
|
-
virtual bool on_message(uint8_t msg_id, const std::vector<uint8_t>& payload) = 0;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @brief Add data to our extension handshake
|
|
60
|
-
*
|
|
61
|
-
* @param handshake The handshake dictionary to add to
|
|
62
|
-
*/
|
|
63
|
-
virtual void add_handshake_data(BencodeDict& handshake) = 0;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @brief Get the message ID the peer uses for this extension
|
|
67
|
-
*/
|
|
68
|
-
uint8_t peer_msg_id() const { return peer_msg_id_; }
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @brief Set the message ID the peer uses
|
|
72
|
-
*/
|
|
73
|
-
void set_peer_msg_id(uint8_t id) { peer_msg_id_ = id; }
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @brief Check if peer supports this extension
|
|
77
|
-
*/
|
|
78
|
-
bool peer_supports() const { return peer_msg_id_ != 0; }
|
|
79
|
-
|
|
80
|
-
protected:
|
|
81
|
-
uint8_t peer_msg_id_ = 0; ///< Message ID in peer's namespace (0 = not supported)
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @brief Manages extension protocol for a connection
|
|
86
|
-
*/
|
|
87
|
-
class ExtensionManager {
|
|
88
|
-
public:
|
|
89
|
-
/**
|
|
90
|
-
* @brief Create extension manager
|
|
91
|
-
* @param conn The peer connection
|
|
92
|
-
*/
|
|
93
|
-
explicit ExtensionManager(BtPeerConnection* conn);
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* @brief Register an extension
|
|
97
|
-
*
|
|
98
|
-
* @param extension The extension to register
|
|
99
|
-
* @param local_id Our local message ID for this extension
|
|
100
|
-
*/
|
|
101
|
-
void register_extension(std::shared_ptr<BtExtension> extension, uint8_t local_id);
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @brief Get extension by name
|
|
105
|
-
*/
|
|
106
|
-
std::shared_ptr<BtExtension> get_extension(const std::string& name);
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* @brief Create the extension handshake message
|
|
110
|
-
* @return Bencoded handshake dictionary
|
|
111
|
-
*/
|
|
112
|
-
std::vector<uint8_t> create_handshake();
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @brief Process received extension handshake
|
|
116
|
-
* @param payload The handshake payload (bencoded dictionary)
|
|
117
|
-
*/
|
|
118
|
-
void process_handshake(const std::vector<uint8_t>& payload);
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @brief Handle an extended message
|
|
122
|
-
*
|
|
123
|
-
* @param extension_id The extension message ID
|
|
124
|
-
* @param payload The message payload
|
|
125
|
-
* @return true if message was handled
|
|
126
|
-
*/
|
|
127
|
-
bool handle_message(uint8_t extension_id, const std::vector<uint8_t>& payload);
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* @brief Send an extension message
|
|
131
|
-
*
|
|
132
|
-
* @param extension_name Name of the extension
|
|
133
|
-
* @param payload Message payload
|
|
134
|
-
*/
|
|
135
|
-
void send_message(const std::string& extension_name, const std::vector<uint8_t>& payload);
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* @brief Get metadata size from handshake (for ut_metadata)
|
|
139
|
-
*/
|
|
140
|
-
size_t metadata_size() const { return metadata_size_; }
|
|
141
|
-
|
|
142
|
-
private:
|
|
143
|
-
BtPeerConnection* conn_;
|
|
144
|
-
std::unordered_map<std::string, std::shared_ptr<BtExtension>> extensions_;
|
|
145
|
-
std::unordered_map<uint8_t, std::shared_ptr<BtExtension>> local_id_map_;
|
|
146
|
-
std::unordered_map<uint8_t, std::shared_ptr<BtExtension>> peer_id_map_;
|
|
147
|
-
size_t metadata_size_ = 0;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
//=============================================================================
|
|
151
|
-
// ut_metadata Extension (BEP 9)
|
|
152
|
-
//=============================================================================
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* @brief Message types for ut_metadata
|
|
156
|
-
*/
|
|
157
|
-
enum class UtMetadataMessageType : uint8_t {
|
|
158
|
-
Request = 0,
|
|
159
|
-
Data = 1,
|
|
160
|
-
Reject = 2
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* @brief ut_metadata extension for exchanging torrent metadata
|
|
165
|
-
*
|
|
166
|
-
* This allows downloading torrent metadata from peers when using magnet links.
|
|
167
|
-
*/
|
|
168
|
-
class UtMetadataExtension : public BtExtension {
|
|
169
|
-
public:
|
|
170
|
-
/// Callback when complete metadata is received
|
|
171
|
-
using MetadataCallback = std::function<void(const std::vector<uint8_t>& metadata)>;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* @brief Create ut_metadata extension
|
|
175
|
-
*
|
|
176
|
-
* @param metadata_size Expected metadata size (from magnet or handshake)
|
|
177
|
-
* @param our_metadata Our metadata (if we have it, for seeding)
|
|
178
|
-
*/
|
|
179
|
-
UtMetadataExtension(size_t metadata_size = 0,
|
|
180
|
-
const std::vector<uint8_t>* our_metadata = nullptr);
|
|
181
|
-
|
|
182
|
-
std::string name() const override { return "ut_metadata"; }
|
|
183
|
-
|
|
184
|
-
void on_handshake(const BencodeDict& handshake) override;
|
|
185
|
-
bool on_message(uint8_t msg_id, const std::vector<uint8_t>& payload) override;
|
|
186
|
-
void add_handshake_data(BencodeDict& handshake) override;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* @brief Set callback for when metadata is complete
|
|
190
|
-
*/
|
|
191
|
-
void set_metadata_callback(MetadataCallback cb) { on_metadata_complete_ = std::move(cb); }
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @brief Request a metadata piece from peer
|
|
195
|
-
* @param piece Piece index (0-based)
|
|
196
|
-
* @return Encoded request message
|
|
197
|
-
*/
|
|
198
|
-
std::vector<uint8_t> create_request(uint32_t piece);
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @brief Create a data response
|
|
202
|
-
* @param piece Piece index
|
|
203
|
-
* @param data Piece data
|
|
204
|
-
* @return Encoded data message
|
|
205
|
-
*/
|
|
206
|
-
std::vector<uint8_t> create_data(uint32_t piece, const std::vector<uint8_t>& data);
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* @brief Create a reject response
|
|
210
|
-
* @param piece Piece index
|
|
211
|
-
* @return Encoded reject message
|
|
212
|
-
*/
|
|
213
|
-
std::vector<uint8_t> create_reject(uint32_t piece);
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* @brief Get number of metadata pieces
|
|
217
|
-
*/
|
|
218
|
-
uint32_t num_pieces() const;
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @brief Check if we have complete metadata
|
|
222
|
-
*/
|
|
223
|
-
bool have_metadata() const { return metadata_complete_; }
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @brief Get the assembled metadata
|
|
227
|
-
*/
|
|
228
|
-
const std::vector<uint8_t>& get_metadata() const { return received_metadata_; }
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* @brief Get next piece to request
|
|
232
|
-
* @return Piece index, or num_pieces() if all requested
|
|
233
|
-
*/
|
|
234
|
-
uint32_t next_piece_to_request() const;
|
|
235
|
-
|
|
236
|
-
private:
|
|
237
|
-
void handle_request(const BencodeDict& msg);
|
|
238
|
-
void handle_data(const BencodeDict& msg, const std::vector<uint8_t>& payload);
|
|
239
|
-
void handle_reject(const BencodeDict& msg);
|
|
240
|
-
void check_complete();
|
|
241
|
-
|
|
242
|
-
size_t metadata_size_;
|
|
243
|
-
const std::vector<uint8_t>* our_metadata_;
|
|
244
|
-
|
|
245
|
-
std::vector<uint8_t> received_metadata_;
|
|
246
|
-
std::vector<bool> pieces_received_;
|
|
247
|
-
std::vector<bool> pieces_requested_;
|
|
248
|
-
bool metadata_complete_;
|
|
249
|
-
|
|
250
|
-
MetadataCallback on_metadata_complete_;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
//=============================================================================
|
|
254
|
-
// ut_pex Extension (Peer Exchange)
|
|
255
|
-
//=============================================================================
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* @brief Peer address for PEX
|
|
259
|
-
*/
|
|
260
|
-
struct PexPeer {
|
|
261
|
-
std::string ip;
|
|
262
|
-
uint16_t port;
|
|
263
|
-
bool supports_encryption;
|
|
264
|
-
bool supports_utp;
|
|
265
|
-
|
|
266
|
-
PexPeer() : port(0), supports_encryption(false), supports_utp(false) {}
|
|
267
|
-
PexPeer(const std::string& i, uint16_t p)
|
|
268
|
-
: ip(i), port(p), supports_encryption(false), supports_utp(false) {}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* @brief ut_pex extension for peer exchange
|
|
273
|
-
*/
|
|
274
|
-
class UtPexExtension : public BtExtension {
|
|
275
|
-
public:
|
|
276
|
-
/// Callback for received peers
|
|
277
|
-
using PeersCallback = std::function<void(const std::vector<PexPeer>& added,
|
|
278
|
-
const std::vector<PexPeer>& dropped)>;
|
|
279
|
-
|
|
280
|
-
UtPexExtension();
|
|
281
|
-
|
|
282
|
-
std::string name() const override { return "ut_pex"; }
|
|
283
|
-
|
|
284
|
-
void on_handshake(const BencodeDict& handshake) override;
|
|
285
|
-
bool on_message(uint8_t msg_id, const std::vector<uint8_t>& payload) override;
|
|
286
|
-
void add_handshake_data(BencodeDict& handshake) override;
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* @brief Set callback for received peers
|
|
290
|
-
*/
|
|
291
|
-
void set_peers_callback(PeersCallback cb) { on_peers_ = std::move(cb); }
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* @brief Create a PEX message
|
|
295
|
-
*
|
|
296
|
-
* @param added Newly added peers
|
|
297
|
-
* @param dropped Recently dropped peers
|
|
298
|
-
* @return Encoded PEX message
|
|
299
|
-
*/
|
|
300
|
-
std::vector<uint8_t> create_message(const std::vector<PexPeer>& added,
|
|
301
|
-
const std::vector<PexPeer>& dropped);
|
|
302
|
-
|
|
303
|
-
private:
|
|
304
|
-
std::vector<PexPeer> parse_peers(const std::string& compact_peers);
|
|
305
|
-
|
|
306
|
-
PeersCallback on_peers_;
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
} // namespace librats
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
#include "bt_file_storage.h"
|
|
2
|
-
#include <algorithm>
|
|
3
|
-
#include <stdexcept>
|
|
4
|
-
|
|
5
|
-
namespace librats {
|
|
6
|
-
|
|
7
|
-
//=============================================================================
|
|
8
|
-
// Constructors
|
|
9
|
-
//=============================================================================
|
|
10
|
-
|
|
11
|
-
FileStorage::FileStorage()
|
|
12
|
-
: total_size_(0)
|
|
13
|
-
, piece_length_(0)
|
|
14
|
-
, num_pieces_(0)
|
|
15
|
-
, finalized_(false) {
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
FileStorage::FileStorage(uint32_t piece_length)
|
|
19
|
-
: total_size_(0)
|
|
20
|
-
, piece_length_(piece_length)
|
|
21
|
-
, num_pieces_(0)
|
|
22
|
-
, finalized_(false) {
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
//=============================================================================
|
|
26
|
-
// File Management
|
|
27
|
-
//=============================================================================
|
|
28
|
-
|
|
29
|
-
void FileStorage::add_file(const std::string& path, int64_t size,
|
|
30
|
-
bool pad_file, bool executable, bool hidden) {
|
|
31
|
-
if (finalized_) {
|
|
32
|
-
return; // Cannot add files after finalization
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
FileEntry entry;
|
|
36
|
-
entry.path = path;
|
|
37
|
-
entry.size = size;
|
|
38
|
-
entry.offset = total_size_;
|
|
39
|
-
entry.pad_file = pad_file;
|
|
40
|
-
entry.executable = executable;
|
|
41
|
-
entry.hidden = hidden;
|
|
42
|
-
|
|
43
|
-
files_.push_back(std::move(entry));
|
|
44
|
-
total_size_ += size;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void FileStorage::add_file(const FileEntry& entry) {
|
|
48
|
-
if (finalized_) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
FileEntry new_entry = entry;
|
|
53
|
-
new_entry.offset = total_size_;
|
|
54
|
-
|
|
55
|
-
files_.push_back(std::move(new_entry));
|
|
56
|
-
total_size_ += entry.size;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
void FileStorage::reserve(size_t num_files) {
|
|
60
|
-
files_.reserve(num_files);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
void FileStorage::set_piece_length(uint32_t length) {
|
|
64
|
-
piece_length_ = length;
|
|
65
|
-
if (finalized_ && piece_length_ > 0) {
|
|
66
|
-
// Recalculate number of pieces
|
|
67
|
-
num_pieces_ = static_cast<uint32_t>((total_size_ + piece_length_ - 1) / piece_length_);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
void FileStorage::finalize() {
|
|
72
|
-
if (piece_length_ > 0 && total_size_ > 0) {
|
|
73
|
-
num_pieces_ = static_cast<uint32_t>((total_size_ + piece_length_ - 1) / piece_length_);
|
|
74
|
-
} else {
|
|
75
|
-
num_pieces_ = 0;
|
|
76
|
-
}
|
|
77
|
-
finalized_ = true;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
//=============================================================================
|
|
81
|
-
// Accessors
|
|
82
|
-
//=============================================================================
|
|
83
|
-
|
|
84
|
-
uint32_t FileStorage::piece_size(uint32_t piece_index) const {
|
|
85
|
-
if (piece_index >= num_pieces_) {
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Last piece may be smaller
|
|
90
|
-
if (piece_index == num_pieces_ - 1) {
|
|
91
|
-
int64_t remaining = total_size_ - static_cast<int64_t>(piece_index) * piece_length_;
|
|
92
|
-
return static_cast<uint32_t>(remaining);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return piece_length_;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
//=============================================================================
|
|
99
|
-
// Piece-to-File Mapping
|
|
100
|
-
//=============================================================================
|
|
101
|
-
|
|
102
|
-
std::vector<FileSlice> FileStorage::map_block(uint32_t piece, uint32_t offset, uint32_t size) const {
|
|
103
|
-
std::vector<FileSlice> result;
|
|
104
|
-
|
|
105
|
-
if (!finalized_ || files_.empty() || piece >= num_pieces_) {
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Calculate absolute offset in torrent
|
|
110
|
-
int64_t torrent_offset = static_cast<int64_t>(piece) * piece_length_ + offset;
|
|
111
|
-
|
|
112
|
-
// Clamp size to not exceed total size
|
|
113
|
-
if (torrent_offset + size > total_size_) {
|
|
114
|
-
size = static_cast<uint32_t>(total_size_ - torrent_offset);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (size == 0) {
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Find starting file
|
|
122
|
-
size_t file_idx = find_file_at_offset(torrent_offset);
|
|
123
|
-
if (file_idx >= files_.size()) {
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
int64_t remaining = size;
|
|
128
|
-
int64_t current_offset = torrent_offset;
|
|
129
|
-
|
|
130
|
-
while (remaining > 0 && file_idx < files_.size()) {
|
|
131
|
-
const FileEntry& file = files_[file_idx];
|
|
132
|
-
|
|
133
|
-
// Offset within this file
|
|
134
|
-
int64_t file_offset = current_offset - file.offset;
|
|
135
|
-
|
|
136
|
-
// How much can we read from this file?
|
|
137
|
-
int64_t bytes_in_file = file.size - file_offset;
|
|
138
|
-
int64_t bytes_to_read = (std::min)(remaining, bytes_in_file);
|
|
139
|
-
|
|
140
|
-
if (bytes_to_read > 0 && !file.pad_file) {
|
|
141
|
-
result.emplace_back(file_idx, file_offset, bytes_to_read);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
current_offset += bytes_to_read;
|
|
145
|
-
remaining -= bytes_to_read;
|
|
146
|
-
++file_idx;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return result;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
size_t FileStorage::file_at_offset(int64_t torrent_offset) const {
|
|
153
|
-
if (torrent_offset < 0 || torrent_offset >= total_size_) {
|
|
154
|
-
return files_.size();
|
|
155
|
-
}
|
|
156
|
-
return find_file_at_offset(torrent_offset);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
size_t FileStorage::file_at_piece(uint32_t piece) const {
|
|
160
|
-
if (piece >= num_pieces_) {
|
|
161
|
-
return files_.size();
|
|
162
|
-
}
|
|
163
|
-
int64_t offset = static_cast<int64_t>(piece) * piece_length_;
|
|
164
|
-
return find_file_at_offset(offset);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
size_t FileStorage::find_file_at_offset(int64_t offset) const {
|
|
168
|
-
if (files_.empty()) {
|
|
169
|
-
return 0;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Binary search for file containing this offset
|
|
173
|
-
size_t left = 0;
|
|
174
|
-
size_t right = files_.size();
|
|
175
|
-
|
|
176
|
-
while (left < right) {
|
|
177
|
-
size_t mid = left + (right - left) / 2;
|
|
178
|
-
const FileEntry& file = files_[mid];
|
|
179
|
-
|
|
180
|
-
if (offset < file.offset) {
|
|
181
|
-
right = mid;
|
|
182
|
-
} else if (offset >= file.offset + file.size) {
|
|
183
|
-
left = mid + 1;
|
|
184
|
-
} else {
|
|
185
|
-
return mid;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Clamp to last file if offset is at the very end
|
|
190
|
-
if (left >= files_.size() && !files_.empty()) {
|
|
191
|
-
return files_.size() - 1;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return left;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
//=============================================================================
|
|
198
|
-
// File-to-Piece Mapping
|
|
199
|
-
//=============================================================================
|
|
200
|
-
|
|
201
|
-
PiecePosition FileStorage::map_file(size_t file_index, int64_t file_offset, uint32_t size) const {
|
|
202
|
-
if (file_index >= files_.size() || !finalized_) {
|
|
203
|
-
return PiecePosition();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const FileEntry& file = files_[file_index];
|
|
207
|
-
|
|
208
|
-
// Clamp file_offset
|
|
209
|
-
if (file_offset < 0) file_offset = 0;
|
|
210
|
-
if (file_offset >= file.size) {
|
|
211
|
-
return PiecePosition();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Calculate absolute torrent offset
|
|
215
|
-
int64_t torrent_offset = file.offset + file_offset;
|
|
216
|
-
|
|
217
|
-
// Calculate piece and offset within piece
|
|
218
|
-
uint32_t piece = static_cast<uint32_t>(torrent_offset / piece_length_);
|
|
219
|
-
uint32_t piece_offset = static_cast<uint32_t>(torrent_offset % piece_length_);
|
|
220
|
-
|
|
221
|
-
// Clamp size to file boundary
|
|
222
|
-
int64_t max_size = file.size - file_offset;
|
|
223
|
-
if (size > max_size) {
|
|
224
|
-
size = static_cast<uint32_t>(max_size);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return PiecePosition(piece, piece_offset, size);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
uint32_t FileStorage::file_first_piece(size_t file_index) const {
|
|
231
|
-
if (file_index >= files_.size() || piece_length_ == 0) {
|
|
232
|
-
return 0;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const FileEntry& file = files_[file_index];
|
|
236
|
-
return static_cast<uint32_t>(file.offset / piece_length_);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
uint32_t FileStorage::file_last_piece(size_t file_index) const {
|
|
240
|
-
if (file_index >= files_.size() || piece_length_ == 0) {
|
|
241
|
-
return 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const FileEntry& file = files_[file_index];
|
|
245
|
-
if (file.size == 0) {
|
|
246
|
-
return file_first_piece(file_index);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
int64_t last_byte = file.offset + file.size - 1;
|
|
250
|
-
return static_cast<uint32_t>(last_byte / piece_length_);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
uint32_t FileStorage::file_num_pieces(size_t file_index) const {
|
|
254
|
-
if (file_index >= files_.size()) {
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return file_last_piece(file_index) - file_first_piece(file_index) + 1;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
} // namespace librats
|