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,103 @@
|
|
|
1
|
+
#include "librats/dht/dht_runner.h"
|
|
2
|
+
#include "librats/dht/node.h"
|
|
3
|
+
#include "librats/dht/udp_transport.h"
|
|
4
|
+
#include "librats/dht/log.h"
|
|
5
|
+
|
|
6
|
+
#include <chrono>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
namespace librats {
|
|
10
|
+
namespace dht {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
constexpr int kRecvTimeoutMs = 100; // loop responsiveness
|
|
14
|
+
constexpr std::chrono::milliseconds kTickInterval{1000}; // Node maintenance cadence
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
DhtRunner::DhtRunner(Node& node, UdpTransport& transport)
|
|
18
|
+
: node_(node), transport_(transport) {}
|
|
19
|
+
|
|
20
|
+
DhtRunner::~DhtRunner() {
|
|
21
|
+
stop();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void DhtRunner::start(std::function<void()> on_loop_entry) {
|
|
25
|
+
if (running_.exchange(true)) return;
|
|
26
|
+
thread_ = std::thread([this, on_loop_entry = std::move(on_loop_entry)] {
|
|
27
|
+
if (on_loop_entry) on_loop_entry();
|
|
28
|
+
loop();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void DhtRunner::stop() {
|
|
33
|
+
// Flip the flag under the queue mutex, so post() can never slip a task past a
|
|
34
|
+
// concurrent stop() and leave it queued to a loop that is already gone.
|
|
35
|
+
{
|
|
36
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
37
|
+
if (!running_.exchange(false)) return;
|
|
38
|
+
}
|
|
39
|
+
wakeup_.signal(); // wake the loop now instead of waiting out the recv timeout
|
|
40
|
+
if (thread_.joinable()) thread_.join();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
bool DhtRunner::post(std::function<void()> task) {
|
|
44
|
+
{
|
|
45
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
46
|
+
// Refuse once stopped: no one is left to drain the queue.
|
|
47
|
+
if (!running_.load()) return false;
|
|
48
|
+
tasks_.push_back(std::move(task));
|
|
49
|
+
}
|
|
50
|
+
// The task is queued before we signal, so whenever a wakeup byte exists the task is
|
|
51
|
+
// already visible to drain_tasks() — the loop can never block past a pending task.
|
|
52
|
+
wakeup_.signal();
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void DhtRunner::set_periodic(std::chrono::milliseconds interval, std::function<void()> fn) {
|
|
57
|
+
periodic_interval_ = interval;
|
|
58
|
+
periodic_fn_ = std::move(fn);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void DhtRunner::loop() {
|
|
62
|
+
const auto start = std::chrono::steady_clock::now();
|
|
63
|
+
auto last_tick = start;
|
|
64
|
+
auto last_periodic = start; // first periodic fires one full interval in, not at t=0
|
|
65
|
+
while (running_.load()) {
|
|
66
|
+
Address from;
|
|
67
|
+
// recv() returns early (as nullopt) the moment post()/stop() signals the wakeup
|
|
68
|
+
// pipe, so a posted task runs without waiting out kRecvTimeoutMs.
|
|
69
|
+
auto data = transport_.recv(kRecvTimeoutMs, from, wakeup_.fd());
|
|
70
|
+
const auto now = std::chrono::steady_clock::now();
|
|
71
|
+
|
|
72
|
+
// Consume the wakeup byte(s) before processing, so the next recv() blocks again.
|
|
73
|
+
// Draining here (not after drain_tasks) means any task posted during processing
|
|
74
|
+
// leaves an un-drained byte that wakes the following recv() — no lost wakeups.
|
|
75
|
+
wakeup_.drain();
|
|
76
|
+
|
|
77
|
+
if (data) node_.on_datagram(*data, from, now);
|
|
78
|
+
|
|
79
|
+
drain_tasks();
|
|
80
|
+
|
|
81
|
+
if (now - last_tick >= kTickInterval) {
|
|
82
|
+
node_.tick(now);
|
|
83
|
+
last_tick = now;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (periodic_fn_ && periodic_interval_.count() > 0 && now - last_periodic >= periodic_interval_) {
|
|
87
|
+
periodic_fn_();
|
|
88
|
+
last_periodic = now;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void DhtRunner::drain_tasks() {
|
|
94
|
+
std::vector<std::function<void()>> pending;
|
|
95
|
+
{
|
|
96
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
97
|
+
pending.swap(tasks_);
|
|
98
|
+
}
|
|
99
|
+
for (auto& task : pending) task();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
} // namespace dht
|
|
103
|
+
} // namespace librats
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file dht_runner.h
|
|
5
|
+
* @brief The single thread that drives a dht::Node over a real socket.
|
|
6
|
+
*
|
|
7
|
+
* This is where real time and threading enter the otherwise pure, single-threaded
|
|
8
|
+
* Node. The loop pumps incoming datagrams into Node::on_datagram, runs Node::tick on
|
|
9
|
+
* a fixed cadence, and executes tasks posted from other threads — all on this one
|
|
10
|
+
* thread, so the Node itself never needs a lock. External callers must reach the Node
|
|
11
|
+
* through post() rather than touching it directly.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#include "librats/core/wakeup_pipe.h"
|
|
15
|
+
|
|
16
|
+
#include <atomic>
|
|
17
|
+
#include <chrono>
|
|
18
|
+
#include <functional>
|
|
19
|
+
#include <mutex>
|
|
20
|
+
#include <thread>
|
|
21
|
+
#include <vector>
|
|
22
|
+
|
|
23
|
+
namespace librats {
|
|
24
|
+
namespace dht {
|
|
25
|
+
|
|
26
|
+
class Node;
|
|
27
|
+
class UdpTransport;
|
|
28
|
+
|
|
29
|
+
class DhtRunner {
|
|
30
|
+
public:
|
|
31
|
+
DhtRunner(Node& node, UdpTransport& transport);
|
|
32
|
+
~DhtRunner();
|
|
33
|
+
|
|
34
|
+
DhtRunner(const DhtRunner&) = delete;
|
|
35
|
+
DhtRunner& operator=(const DhtRunner&) = delete;
|
|
36
|
+
|
|
37
|
+
// `on_loop_entry`, if given, runs on the loop thread before anything else does —
|
|
38
|
+
// the hook for marking that thread as such (see DhtClient), so even the very first
|
|
39
|
+
// callback it runs is already recognisable.
|
|
40
|
+
void start(std::function<void()> on_loop_entry = {});
|
|
41
|
+
void stop();
|
|
42
|
+
|
|
43
|
+
// Run `task` on the loop thread. The only safe way to touch the Node from outside.
|
|
44
|
+
// Returns false (dropping the task) once the runner is stopped: nothing would ever
|
|
45
|
+
// run it, so a caller must not be left believing it was queued.
|
|
46
|
+
bool post(std::function<void()> task);
|
|
47
|
+
|
|
48
|
+
// Register a callback invoked on the loop thread every `interval` (e.g. persisting
|
|
49
|
+
// the routing table). The first invocation is one interval after start(), never at
|
|
50
|
+
// t=0. Must be set before start(); call with a null fn to disable. The runner stays
|
|
51
|
+
// oblivious to what the work is — the I/O concern lives in the caller.
|
|
52
|
+
void set_periodic(std::chrono::milliseconds interval, std::function<void()> fn);
|
|
53
|
+
|
|
54
|
+
private:
|
|
55
|
+
void loop();
|
|
56
|
+
void drain_tasks();
|
|
57
|
+
|
|
58
|
+
Node& node_;
|
|
59
|
+
UdpTransport& transport_;
|
|
60
|
+
std::thread thread_;
|
|
61
|
+
std::atomic<bool> running_{false};
|
|
62
|
+
std::mutex mutex_;
|
|
63
|
+
std::vector<std::function<void()>> tasks_;
|
|
64
|
+
WakeupPipe wakeup_; // lets post()/stop() interrupt the recv() wait immediately
|
|
65
|
+
|
|
66
|
+
std::chrono::milliseconds periodic_interval_{0}; // 0 = disabled
|
|
67
|
+
std::function<void()> periodic_fn_;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
} // namespace dht
|
|
71
|
+
} // namespace librats
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#include "librats/dht/dos_blocker.h"
|
|
2
|
+
#include "librats/dht/log.h"
|
|
3
|
+
|
|
4
|
+
namespace librats {
|
|
5
|
+
namespace dht {
|
|
6
|
+
|
|
7
|
+
bool DosBlocker::allow(const IpAddress& ip, TimePoint now) {
|
|
8
|
+
Entry& e = table_[ip];
|
|
9
|
+
|
|
10
|
+
if (e.banned_until > now) return false; // still serving a ban
|
|
11
|
+
|
|
12
|
+
if (now - e.window_start >= kWindow) { // start of a fresh window
|
|
13
|
+
e.window_start = now;
|
|
14
|
+
e.count = 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (++e.count > kMaxPerWindow) {
|
|
18
|
+
e.banned_until = now + kBanDuration;
|
|
19
|
+
// Logged once at the ban transition only — the top-of-function early return keeps
|
|
20
|
+
// every subsequent packet from this IP from re-logging. DEBUG (not WARN) on purpose:
|
|
21
|
+
// a spoofed-source flood could otherwise turn one ban per IP into log amplification.
|
|
22
|
+
LOG_DEBUG("dht", "rate-limit ban: " << ip.to_string() << " (" << e.count << " queries in window)");
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (table_.size() > kMaxTracked) prune(now);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
void DosBlocker::prune(TimePoint now) {
|
|
31
|
+
// Drop idle, unbanned entries; they carry no state worth keeping.
|
|
32
|
+
for (auto it = table_.begin(); it != table_.end();) {
|
|
33
|
+
const Entry& e = it->second;
|
|
34
|
+
if (e.banned_until <= now && now - e.window_start >= kWindow)
|
|
35
|
+
it = table_.erase(it);
|
|
36
|
+
else
|
|
37
|
+
++it;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
} // namespace dht
|
|
42
|
+
} // namespace librats
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file dos_blocker.h
|
|
5
|
+
* @brief Per-IP flood protection for the DHT's incoming path.
|
|
6
|
+
*
|
|
7
|
+
* A peer is allowed a burst of packets per short window; cross the threshold and it's
|
|
8
|
+
* banned for a while. Cheap and bounded — a single counter + two timestamps per IP,
|
|
9
|
+
* with the tracking table capped. Consulted before any incoming packet is processed.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "librats/core/ip_address.h"
|
|
13
|
+
#include "librats/dht/observer.h" // for TimePoint
|
|
14
|
+
|
|
15
|
+
#include <chrono>
|
|
16
|
+
#include <cstddef>
|
|
17
|
+
#include <unordered_map>
|
|
18
|
+
|
|
19
|
+
namespace librats {
|
|
20
|
+
namespace dht {
|
|
21
|
+
|
|
22
|
+
class DosBlocker {
|
|
23
|
+
public:
|
|
24
|
+
static constexpr int kMaxPerWindow = 50; // packets per window before a ban
|
|
25
|
+
static constexpr std::chrono::seconds kWindow{10};
|
|
26
|
+
static constexpr std::chrono::minutes kBanDuration{5};
|
|
27
|
+
static constexpr std::size_t kMaxTracked = 2000; // cap the per-IP table
|
|
28
|
+
|
|
29
|
+
// True if a packet from `ip` should be processed; false if it's being rate-limited.
|
|
30
|
+
bool allow(const IpAddress& ip, TimePoint now);
|
|
31
|
+
|
|
32
|
+
std::size_t tracked() const noexcept { return table_.size(); }
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
struct Entry {
|
|
36
|
+
int count = 0;
|
|
37
|
+
TimePoint window_start{};
|
|
38
|
+
TimePoint banned_until{};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
void prune(TimePoint now);
|
|
42
|
+
|
|
43
|
+
std::unordered_map<IpAddress, Entry> table_;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} // namespace dht
|
|
47
|
+
} // namespace librats
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#include "librats/dht/find_peers.h"
|
|
2
|
+
#include "librats/dht/krpc.h"
|
|
3
|
+
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <utility>
|
|
6
|
+
|
|
7
|
+
namespace librats {
|
|
8
|
+
namespace dht {
|
|
9
|
+
|
|
10
|
+
FindPeers::FindPeers(RoutingTable& table, RpcManager& rpc, const NodeId& self,
|
|
11
|
+
const InfoHash& info_hash, PeersCallback on_peers, DoneCallback on_done)
|
|
12
|
+
: Traversal(table, rpc, self, info_hash),
|
|
13
|
+
on_peers_(std::move(on_peers)),
|
|
14
|
+
on_done_(std::move(on_done)) {}
|
|
15
|
+
|
|
16
|
+
void FindPeers::got_peers(const std::vector<Address>& peers) {
|
|
17
|
+
std::vector<Address> fresh;
|
|
18
|
+
for (const auto& p : peers) {
|
|
19
|
+
if (std::find(peers_.begin(), peers_.end(), p) == peers_.end()) {
|
|
20
|
+
peers_.push_back(p);
|
|
21
|
+
fresh.push_back(p);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!fresh.empty() && on_peers_) on_peers_(fresh);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
void FindPeers::got_token(const NodeId& id, const std::string& token) {
|
|
28
|
+
if (token.empty()) return;
|
|
29
|
+
tokens_.emplace(id, token); // first token from a node wins
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bool FindPeers::invoke(const ObserverPtr& o, TimePoint now) {
|
|
33
|
+
KrpcMessage msg = KrpcProtocol::create_get_peers_query(/*txn set by rpc*/ "", self_, target());
|
|
34
|
+
msg.want = want_; // BEP 32: ask for our own family's nodes
|
|
35
|
+
return rpc_.invoke(msg, o->endpoint(), o, now);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ObserverPtr FindPeers::make_observer(const NodeId& id, const Address& ep) {
|
|
39
|
+
return std::make_shared<FindPeersObserver>(*this, id, ep);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void FindPeers::on_complete() {
|
|
43
|
+
if (on_done_) on_done_(peers_);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void FindPeersObserver::parse_reply(const KrpcMessage& msg) {
|
|
47
|
+
if (!msg.token.empty()) owner_.got_token(msg.response_id, msg.token);
|
|
48
|
+
if (!msg.peers.empty()) owner_.got_peers(msg.peers);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
} // namespace dht
|
|
52
|
+
} // namespace librats
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file find_peers.h
|
|
5
|
+
* @brief get_peers lookup: find the peers announced under an info-hash (BEP 5).
|
|
6
|
+
*
|
|
7
|
+
* An iterative lookup toward the info-hash. Along the way it collects peer
|
|
8
|
+
* endpoints from "values" replies (delivered incrementally, then once more in full
|
|
9
|
+
* on completion) and the write tokens nodes hand out — the tokens are what an
|
|
10
|
+
* announce needs, so Announce builds directly on this.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
#include "librats/dht/id.h"
|
|
14
|
+
#include "librats/dht/observer.h"
|
|
15
|
+
#include "librats/dht/traversal.h"
|
|
16
|
+
|
|
17
|
+
#include <functional>
|
|
18
|
+
#include <map>
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <vector>
|
|
21
|
+
|
|
22
|
+
namespace librats {
|
|
23
|
+
struct KrpcMessage;
|
|
24
|
+
|
|
25
|
+
namespace dht {
|
|
26
|
+
|
|
27
|
+
class FindPeers : public Traversal {
|
|
28
|
+
public:
|
|
29
|
+
// Called with each batch of newly-discovered peers as the lookup runs.
|
|
30
|
+
using PeersCallback = std::function<void(const std::vector<Address>&)>;
|
|
31
|
+
// Called once when the lookup converges, with every peer found.
|
|
32
|
+
using DoneCallback = std::function<void(const std::vector<Address>&)>;
|
|
33
|
+
|
|
34
|
+
FindPeers(RoutingTable& table, RpcManager& rpc, const NodeId& self, const InfoHash& info_hash,
|
|
35
|
+
PeersCallback on_peers, DoneCallback on_done);
|
|
36
|
+
|
|
37
|
+
void got_peers(const std::vector<Address>& peers); // from a values reply
|
|
38
|
+
void got_token(const NodeId& id, const std::string& token); // from a node's reply
|
|
39
|
+
|
|
40
|
+
// BEP 32: which node families to ask for ("n4" / "n6"). Empty = unspecified.
|
|
41
|
+
void set_want(std::vector<std::string> want) { want_ = std::move(want); }
|
|
42
|
+
|
|
43
|
+
protected:
|
|
44
|
+
bool invoke(const ObserverPtr& o, TimePoint now) override;
|
|
45
|
+
ObserverPtr make_observer(const NodeId& id, const Address& ep) override;
|
|
46
|
+
void on_complete() override;
|
|
47
|
+
const char* name() const override { return "find_peers"; }
|
|
48
|
+
|
|
49
|
+
// The write token each responding node gave us, keyed by node id (Announce reads
|
|
50
|
+
// this). std::map avoids needing a NodeId hash; the map only ever holds ~k entries.
|
|
51
|
+
std::map<NodeId, std::string> tokens_;
|
|
52
|
+
|
|
53
|
+
private:
|
|
54
|
+
PeersCallback on_peers_;
|
|
55
|
+
DoneCallback on_done_;
|
|
56
|
+
std::vector<Address> peers_; // all unique peers found
|
|
57
|
+
std::vector<std::string> want_; // BEP 32 family hint for our queries
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
class FindPeersObserver : public TraversalObserver {
|
|
61
|
+
public:
|
|
62
|
+
FindPeersObserver(FindPeers& owner, const NodeId& id, const Address& ep)
|
|
63
|
+
: TraversalObserver(owner, id, ep), owner_(owner) {}
|
|
64
|
+
|
|
65
|
+
protected:
|
|
66
|
+
void parse_reply(const KrpcMessage& msg) override;
|
|
67
|
+
|
|
68
|
+
private:
|
|
69
|
+
FindPeers& owner_;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
} // namespace dht
|
|
73
|
+
} // namespace librats
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file id.h
|
|
5
|
+
* @brief The 160-bit Kademlia identifier and the pure functions over it.
|
|
6
|
+
*
|
|
7
|
+
* `NodeId` and `InfoHash` share one 160-bit keyspace (BEP 5). Everything here is a
|
|
8
|
+
* header-only, allocation-free leaf primitive — XOR distance, closeness ordering,
|
|
9
|
+
* k-bucket placement, and hex/wire conversions — so the comparators used in the
|
|
10
|
+
* routing table and lookup hot paths can be inlined.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <array>
|
|
15
|
+
#include <cstddef>
|
|
16
|
+
#include <cstdint>
|
|
17
|
+
#include <cstring>
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
#if defined(_MSC_VER)
|
|
21
|
+
#include <intrin.h> // _BitScanReverse for count_leading_zeros
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
namespace librats {
|
|
25
|
+
namespace dht {
|
|
26
|
+
|
|
27
|
+
// Count leading zero bits of a non-zero 32-bit word. One hardware instruction where
|
|
28
|
+
// available; a portable loop otherwise. Precondition: x != 0.
|
|
29
|
+
inline int count_leading_zeros32(uint32_t x) noexcept {
|
|
30
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
31
|
+
return __builtin_clz(x);
|
|
32
|
+
#elif defined(_MSC_VER)
|
|
33
|
+
unsigned long idx;
|
|
34
|
+
_BitScanReverse(&idx, x);
|
|
35
|
+
return 31 - static_cast<int>(idx);
|
|
36
|
+
#else
|
|
37
|
+
int n = 0;
|
|
38
|
+
while ((x & 0x80000000u) == 0) { ++n; x <<= 1; }
|
|
39
|
+
return n;
|
|
40
|
+
#endif
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 160-bit identifier space shared by node ids and info-hashes (Kademlia / BEP 5).
|
|
44
|
+
inline constexpr std::size_t kIdSize = 20; // 160 bits
|
|
45
|
+
inline constexpr std::size_t kBucketSize = 8; // "k": max contacts kept per bucket
|
|
46
|
+
inline constexpr std::size_t kAlpha = 3; // lookup concurrency (branch factor)
|
|
47
|
+
inline constexpr int kDefaultPort = 6881; // standard BitTorrent DHT port
|
|
48
|
+
inline constexpr int kBucketCount = static_cast<int>(kIdSize) * 8; // 160
|
|
49
|
+
|
|
50
|
+
using NodeId = std::array<uint8_t, kIdSize>;
|
|
51
|
+
using InfoHash = NodeId; // same keyspace, named for intent at call sites
|
|
52
|
+
|
|
53
|
+
// Hash functor for using a NodeId/InfoHash as an unordered_map/set key. A node id is
|
|
54
|
+
// already a uniformly-distributed 160-bit value (a SHA-1 digest or random id), so the
|
|
55
|
+
// fastest sound hash is to reinterpret its leading bytes as size_t — no mixing needed.
|
|
56
|
+
// This mirrors libtorrent's std::hash<digest32<N>> (sha1_hash.hpp). Note: most DHT
|
|
57
|
+
// id-keyed tables (write tokens, the peer store) deliberately stay std::map — they're
|
|
58
|
+
// small/cold and ordered is fine; reach for this only on hot, large id sets.
|
|
59
|
+
struct NodeIdHash {
|
|
60
|
+
std::size_t operator()(const NodeId& id) const noexcept {
|
|
61
|
+
std::size_t h;
|
|
62
|
+
std::memcpy(&h, id.data(), sizeof(h));
|
|
63
|
+
return h;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// XOR distance metric d(a, b) = a ^ b (Kademlia).
|
|
68
|
+
inline NodeId distance(const NodeId& a, const NodeId& b) noexcept {
|
|
69
|
+
NodeId d;
|
|
70
|
+
for (std::size_t i = 0; i < kIdSize; ++i) d[i] = a[i] ^ b[i];
|
|
71
|
+
return d;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// True iff `a` is strictly closer to `target` than `b` under the XOR metric.
|
|
75
|
+
// Compares a^target against b^target as 160-bit big-endian numbers without
|
|
76
|
+
// materialising either distance — this is the lookup/sort comparator.
|
|
77
|
+
inline bool closer_to(const NodeId& a, const NodeId& b, const NodeId& target) noexcept {
|
|
78
|
+
for (std::size_t i = 0; i < kIdSize; ++i) {
|
|
79
|
+
const uint8_t da = a[i] ^ target[i];
|
|
80
|
+
const uint8_t db = b[i] ^ target[i];
|
|
81
|
+
if (da != db) return da < db;
|
|
82
|
+
}
|
|
83
|
+
return false; // equal distance is not "strictly closer"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Number of leading bits `a` and `b` share — equivalently, the count of leading
|
|
87
|
+
// zeros of (a ^ b). 0 means they differ in the very first bit (farthest apart);
|
|
88
|
+
// kBucketCount (160) is returned only when the ids are identical. This is the raw
|
|
89
|
+
// keyspace primitive; the routing table caps it to its current bucket count to get
|
|
90
|
+
// an actual bucket index (RoutingTable::bucket_index).
|
|
91
|
+
inline int shared_prefix_bits(const NodeId& a, const NodeId& b) noexcept {
|
|
92
|
+
// Process the 160 bits four bytes at a time (kIdSize == 20 == 5 words). Each word
|
|
93
|
+
// is assembled big-endian so its most significant bit is the earliest id bit, then
|
|
94
|
+
// a single count-leading-zeros pins the first differing bit. Bit-identical to the
|
|
95
|
+
// byte-at-a-time form, ~4x faster: the common case is one word load + one CLZ.
|
|
96
|
+
for (std::size_t i = 0; i < kIdSize; i += 4) {
|
|
97
|
+
const uint32_t d = (static_cast<uint32_t>(a[i] ^ b[i]) << 24)
|
|
98
|
+
| (static_cast<uint32_t>(a[i + 1] ^ b[i + 1]) << 16)
|
|
99
|
+
| (static_cast<uint32_t>(a[i + 2] ^ b[i + 2]) << 8)
|
|
100
|
+
| static_cast<uint32_t>(a[i + 3] ^ b[i + 3]);
|
|
101
|
+
if (d != 0) return static_cast<int>(i) * 8 + count_leading_zeros32(d);
|
|
102
|
+
}
|
|
103
|
+
return kBucketCount; // identical ids share the whole 160-bit prefix
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Read `n` bits from `id` starting at bit index `start` (MSB-first: bit 0 is the top
|
|
107
|
+
// bit of id[0]), returned right-aligned. Bits past the end of the id read as 0. Used
|
|
108
|
+
// to classify a node by the handful of sub-prefix bits that follow a bucket's shared
|
|
109
|
+
// prefix, so `n` is always small (<= 7).
|
|
110
|
+
inline uint32_t bits_at(const NodeId& id, int start, int n) noexcept {
|
|
111
|
+
uint32_t v = 0;
|
|
112
|
+
for (int k = 0; k < n; ++k) {
|
|
113
|
+
const int bit = start + k;
|
|
114
|
+
const int b = (bit >= 0 && bit < kBucketCount)
|
|
115
|
+
? ((id[bit >> 3] >> (7 - (bit & 7))) & 1)
|
|
116
|
+
: 0;
|
|
117
|
+
v = (v << 1) | static_cast<uint32_t>(b);
|
|
118
|
+
}
|
|
119
|
+
return v;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Lowercase 40-char hex. to_hex / from_hex are exact inverses for valid input.
|
|
123
|
+
inline std::string to_hex(const NodeId& id) {
|
|
124
|
+
static constexpr char kDigits[] = "0123456789abcdef";
|
|
125
|
+
std::string out(kIdSize * 2, '0');
|
|
126
|
+
for (std::size_t i = 0; i < kIdSize; ++i) {
|
|
127
|
+
out[i * 2] = kDigits[id[i] >> 4];
|
|
128
|
+
out[i * 2 + 1] = kDigits[id[i] & 0x0f];
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Parse 40 hex chars. Returns a zero-filled id if the input isn't exactly 40 valid
|
|
134
|
+
// hex characters, so a malformed string can never be mistaken for a real id.
|
|
135
|
+
inline NodeId from_hex(const std::string& hex) {
|
|
136
|
+
NodeId id{};
|
|
137
|
+
if (hex.size() != kIdSize * 2) return id;
|
|
138
|
+
const auto nibble = [](char c) -> int {
|
|
139
|
+
if (c >= '0' && c <= '9') return c - '0';
|
|
140
|
+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
141
|
+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
142
|
+
return -1;
|
|
143
|
+
};
|
|
144
|
+
for (std::size_t i = 0; i < kIdSize; ++i) {
|
|
145
|
+
const int hi = nibble(hex[i * 2]);
|
|
146
|
+
const int lo = nibble(hex[i * 2 + 1]);
|
|
147
|
+
if (hi < 0 || lo < 0) return NodeId{};
|
|
148
|
+
id[i] = static_cast<uint8_t>((hi << 4) | lo);
|
|
149
|
+
}
|
|
150
|
+
return id;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Raw 20-byte wire form, as carried in KRPC messages. Inverses of each other.
|
|
154
|
+
inline std::string to_bytes(const NodeId& id) {
|
|
155
|
+
return std::string(reinterpret_cast<const char*>(id.data()), kIdSize);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Returns a zero-filled id if `bytes` isn't exactly 20 bytes.
|
|
159
|
+
inline NodeId from_bytes(const std::string& bytes) {
|
|
160
|
+
NodeId id{};
|
|
161
|
+
if (bytes.size() != kIdSize) return id;
|
|
162
|
+
std::copy(bytes.begin(), bytes.end(), id.begin());
|
|
163
|
+
return id;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
} // namespace dht
|
|
167
|
+
} // namespace librats
|