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,326 @@
|
|
|
1
|
+
#include "librats/dht/traversal.h"
|
|
2
|
+
#include "librats/dht/bep42.h"
|
|
3
|
+
#include "librats/dht/krpc.h"
|
|
4
|
+
#include "librats/dht/log.h"
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <atomic>
|
|
8
|
+
|
|
9
|
+
// Per-wave "dht.traversal" round logging is verbose; gate it behind a compile-time flag
|
|
10
|
+
// so it can be dropped entirely from a build. Default on (true).
|
|
11
|
+
#ifndef DHT_TRAVERSAL_DEBUG
|
|
12
|
+
#define DHT_TRAVERSAL_DEBUG true
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
namespace librats {
|
|
16
|
+
namespace dht {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
// Monotonic lookup-id source. Atomic because v4 and v6 nodes run on separate threads,
|
|
20
|
+
// so ids stay unique across both instances — a single "L<id>" disambiguates everything.
|
|
21
|
+
// Unsigned so wrap-around past 2^32 is defined (benign) rather than signed-overflow UB.
|
|
22
|
+
std::atomic<unsigned> g_lookup_seq{0};
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
Traversal::Traversal(RoutingTable& table, RpcManager& rpc, const NodeId& self, const NodeId& target)
|
|
26
|
+
: table_(table), rpc_(rpc), self_(self), target_(target),
|
|
27
|
+
id_(g_lookup_seq.fetch_add(1, std::memory_order_relaxed)) {}
|
|
28
|
+
|
|
29
|
+
Traversal::~Traversal() {
|
|
30
|
+
// Detach any still-in-flight queries so the RpcManager won't call into a
|
|
31
|
+
// half-destroyed traversal once we're gone.
|
|
32
|
+
for (auto& obs : results_) {
|
|
33
|
+
if (obs->has(Observer::kQueried) && !obs->has(Observer::kAlive) && !obs->has(Observer::kFailed))
|
|
34
|
+
rpc_.cancel(obs.get());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void Traversal::start(TimePoint now) {
|
|
39
|
+
// Seed from the closest contacts we know, including unconfirmed ones so a fresh
|
|
40
|
+
// table can still bootstrap a lookup.
|
|
41
|
+
for (const auto& n : table_.find_closest(target_, kBucketSize, /*include_unconfirmed=*/true))
|
|
42
|
+
add_entry(n.id, n.endpoint);
|
|
43
|
+
if (add_requests(now)) finish();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void Traversal::add_seed(const Address& ep) {
|
|
47
|
+
add_entry(NodeId{}, ep, Observer::kInitial);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void Traversal::add_entry(const NodeId& id, const Address& ep, uint8_t flags) {
|
|
51
|
+
if (done_) return;
|
|
52
|
+
|
|
53
|
+
// Seeds with an unknown id (routers) go to the unsorted tail: we can't place them
|
|
54
|
+
// by distance and there may be several, so we skip the id-based dedup for them.
|
|
55
|
+
if (flags & Observer::kInitial) {
|
|
56
|
+
ObserverPtr seed = make_observer(id, ep);
|
|
57
|
+
seed->set(Observer::kInitial);
|
|
58
|
+
results_.push_back(std::move(seed));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Binary-search the sorted prefix results_[0..sorted_) for the insertion point.
|
|
63
|
+
// A duplicate id has identical XOR distance to the target, so lower_bound lands
|
|
64
|
+
// exactly on it — one search does both the dedup and the placement.
|
|
65
|
+
const auto sorted_end = results_.begin() + sorted_;
|
|
66
|
+
auto pos = std::lower_bound(results_.begin(), sorted_end, id,
|
|
67
|
+
[&](const ObserverPtr& o, const NodeId& nid) { return closer_to(o->id(), nid, target_); });
|
|
68
|
+
if (pos != sorted_end && (*pos)->id() == id) return; // already a candidate
|
|
69
|
+
|
|
70
|
+
// Anti-Sybil (libtorrent's dht_restrict_search_ips): a genuinely new candidate — the
|
|
71
|
+
// dedup above already let same-id repeats out — sharing a /24 (v4) or /64 (v6) with one
|
|
72
|
+
// we've already admitted is ignored, so no single operator can pack the candidate set
|
|
73
|
+
// and steer the search. The first node in a block wins. This mirrors libtorrent, which
|
|
74
|
+
// runs the check *after* the id dedup and skips it for kInitial seeds (handled above).
|
|
75
|
+
// The packed prefixes live in a per-lookup set, so it is an O(1) test rather than an
|
|
76
|
+
// O(n) scan of results_, and a prefix persists for the lookup's lifetime (a candidate
|
|
77
|
+
// dropped by truncation can't re-open its block). Only public IPs are restricted, so a
|
|
78
|
+
// LAN/loopback/CGNAT test topology is unaffected.
|
|
79
|
+
if (register_subnet(ep)) return;
|
|
80
|
+
|
|
81
|
+
results_.insert(pos, make_observer(id, ep));
|
|
82
|
+
++sorted_;
|
|
83
|
+
|
|
84
|
+
// Cap the candidate list; abandon anything past the cutoff, releasing in-flight
|
|
85
|
+
// queries so they neither leak nor report back later.
|
|
86
|
+
if (results_.size() > kMaxResults) {
|
|
87
|
+
for (std::size_t i = kMaxResults; i < results_.size(); ++i) {
|
|
88
|
+
Observer* o = results_[i].get();
|
|
89
|
+
const bool in_flight = o->has(Observer::kQueried) && !o->has(Observer::kAlive) &&
|
|
90
|
+
!o->has(Observer::kFailed) && !o->has(Observer::kDone);
|
|
91
|
+
if (in_flight) {
|
|
92
|
+
o->set(Observer::kDone);
|
|
93
|
+
if (invoke_count_ > 0) --invoke_count_;
|
|
94
|
+
if (o->has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
|
|
95
|
+
--branch_factor_;
|
|
96
|
+
rpc_.cancel(o);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
results_.resize(kMaxResults);
|
|
100
|
+
if (sorted_ > static_cast<int>(kMaxResults)) sorted_ = static_cast<int>(kMaxResults);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
bool Traversal::register_subnet(const Address& ep) {
|
|
105
|
+
// Only public addresses are constrained; a LAN/loopback/CGNAT topology is exempt (the
|
|
106
|
+
// same rule the routing table applies) and never collides. is_public_address implies a
|
|
107
|
+
// specified v4/v6 address, so bytes() below has the octets we index.
|
|
108
|
+
if (!is_public_address(ep.ip)) return false;
|
|
109
|
+
|
|
110
|
+
const uint8_t* b = ep.ip.bytes().data(); // network-order octets
|
|
111
|
+
if (ep.ip.is_v6()) {
|
|
112
|
+
// /64: the leading 8 bytes packed big-endian (matches ip_too_close's compare and
|
|
113
|
+
// libtorrent's read_uint64 over the first 8 v6 octets).
|
|
114
|
+
uint64_t prefix = 0;
|
|
115
|
+
for (int i = 0; i < 8; ++i) prefix = (prefix << 8) | b[i];
|
|
116
|
+
return !subnet6_.insert(prefix).second; // insert() fails ⇒ block already admitted
|
|
117
|
+
}
|
|
118
|
+
// /24: the leading 3 bytes, low octet dropped — libtorrent's to_uint() & 0xffffff00.
|
|
119
|
+
const uint32_t prefix = (uint32_t(b[0]) << 24) | (uint32_t(b[1]) << 16) | (uint32_t(b[2]) << 8);
|
|
120
|
+
return !subnet4_.insert(prefix).second;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
bool Traversal::add_requests(TimePoint now) {
|
|
124
|
+
if (done_) return true;
|
|
125
|
+
|
|
126
|
+
// How many of the k closest still need a live answer before the search has converged.
|
|
127
|
+
// Starts at k and counts down as we meet alive nodes walking the list closest-first;
|
|
128
|
+
// reaching 0 means the k nodes nearest the target have all replied.
|
|
129
|
+
int alive_needed = static_cast<int>(kBucketSize);
|
|
130
|
+
// Queries among the k closest that we still genuinely wait on: sent, not yet answered,
|
|
131
|
+
// and not yet past their short timeout. A query that has already passed its short
|
|
132
|
+
// timeout is deliberately NOT counted here — a stale straggler must not block completion
|
|
133
|
+
// (see the return below). A just-sent query counts too.
|
|
134
|
+
int in_flight = 0;
|
|
135
|
+
int alive = 0; // alive nodes seen among the closest (for the round log line)
|
|
136
|
+
int sent = 0; // queries fired in this pass (a stall sends nothing)
|
|
137
|
+
|
|
138
|
+
for (auto& obs_ptr : results_) {
|
|
139
|
+
if (alive_needed <= 0) break; // the k closest have all replied — stop scanning
|
|
140
|
+
Observer* o = obs_ptr.get();
|
|
141
|
+
|
|
142
|
+
if (o->has(Observer::kAlive)) { --alive_needed; ++alive; continue; }
|
|
143
|
+
if (o->has(Observer::kFailed) || o->has(Observer::kDone)) continue;
|
|
144
|
+
if (o->has(Observer::kQueried)) {
|
|
145
|
+
// Only a *fresh* query — one that hasn't yet passed its short timeout — blocks
|
|
146
|
+
// completion. Once a query is past its short timeout it has almost certainly
|
|
147
|
+
// gone dead; holding the whole lookup for it until the 15 s full timeout is
|
|
148
|
+
// pure dead time. Such a node gave us no peers, and (being silent) no write
|
|
149
|
+
// token, so it was never a candidate to announce to either — waiting for it
|
|
150
|
+
// cannot improve the result. We keep the observer registered so a late reply
|
|
151
|
+
// is still welcome, but a stale query no longer gates convergence.
|
|
152
|
+
if (!o->has(Observer::kShortTimeout)) ++in_flight;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (invoke_count_ >= branch_factor_) continue; // no free slot; keep scanning to count
|
|
157
|
+
|
|
158
|
+
o->set(Observer::kQueried);
|
|
159
|
+
if (invoke(obs_ptr, now)) {
|
|
160
|
+
++invoke_count_;
|
|
161
|
+
++in_flight;
|
|
162
|
+
++sent;
|
|
163
|
+
} else {
|
|
164
|
+
o->set(Observer::kFailed);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// One line per wave that actually fired queries — this is the search "round". A stall
|
|
169
|
+
// (slots full, nothing sent) prints nothing, so the cadence of these lines reveals
|
|
170
|
+
// timeout-bound progress; `closest` (shared-prefix bits with the target) climbing shows
|
|
171
|
+
// real convergence, while a flat `closest` with a rising `branch` means we're spinning
|
|
172
|
+
// on dead/sybil nodes. Built only when DEBUG is on (the macro guards the level).
|
|
173
|
+
if (sent > 0) {
|
|
174
|
+
++round_;
|
|
175
|
+
#if DHT_TRAVERSAL_DEBUG
|
|
176
|
+
const int closest = sorted_ > 0 ? shared_prefix_bits(results_.front()->id(), target_) : 0;
|
|
177
|
+
LOG_DEBUG("dht.traversal", name() << " L" << id_ << ' ' << short_hex(target_) << " round "
|
|
178
|
+
<< round_ << ": +" << sent << " sent, " << invoke_count_
|
|
179
|
+
<< " in-flight, " << alive << '/' << kBucketSize << " alive, branch="
|
|
180
|
+
<< branch_factor_ << ", closest +" << closest << "b");
|
|
181
|
+
#endif
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Done exactly when nothing we still wait on is in flight. This one check already covers
|
|
185
|
+
// both ways a lookup can end:
|
|
186
|
+
// - success: the k closest have all replied, so none of them is still in flight;
|
|
187
|
+
// - exhausted: a sparse/dead neighbourhood we can't fill to k live nodes — but if any
|
|
188
|
+
// reachable candidate were left, the loop above would have just queried it
|
|
189
|
+
// (which bumps in_flight), so in_flight == 0 also means "nothing left to
|
|
190
|
+
// try". (A stale query frees its slot, so it never hides a sendable one.)
|
|
191
|
+
// A fresh query always keeps in_flight > 0, so we never stop while a node might still
|
|
192
|
+
// answer inside its short timeout. Conversely a *stale* query — one past its short
|
|
193
|
+
// timeout, so almost certainly dead — is excluded from in_flight on purpose: that is what
|
|
194
|
+
// decouples termination from the 15 s full timeout. The full timeout now only ages out
|
|
195
|
+
// routing-table liveness (node_failed), never lookup completion, so one silent-but-close
|
|
196
|
+
// node can no longer stall the search (nor delay an announce_peer) by sitting out its
|
|
197
|
+
// full timeout.
|
|
198
|
+
return in_flight == 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void Traversal::on_responded(Observer& o, uint16_t rtt, TimePoint now) {
|
|
202
|
+
if (done_) return;
|
|
203
|
+
// The reply already set kAlive (see TraversalObserver::on_response); the rtt and
|
|
204
|
+
// alive flag let the routing table record a confirmed contact.
|
|
205
|
+
if (o.has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
|
|
206
|
+
--branch_factor_;
|
|
207
|
+
if (invoke_count_ > 0) --invoke_count_;
|
|
208
|
+
|
|
209
|
+
// A reply makes this a confirmed contact (its real id was resolved in on_response).
|
|
210
|
+
// BEP 42: prefer contacts whose id is derived from their IP.
|
|
211
|
+
table_.node_seen(o.id(), o.endpoint(), rtt, verify_node_id_for_ip(o.id(), o.endpoint().ip));
|
|
212
|
+
|
|
213
|
+
if (add_requests(now)) finish();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void Traversal::on_failed(Observer& o, bool short_timeout, TimePoint now) {
|
|
217
|
+
if (done_) return;
|
|
218
|
+
if (o.has(Observer::kDone) || o.has(Observer::kAlive) || o.has(Observer::kFailed)) return;
|
|
219
|
+
|
|
220
|
+
if (short_timeout) {
|
|
221
|
+
if (!o.has(Observer::kShortTimeout)) {
|
|
222
|
+
o.set(Observer::kShortTimeout);
|
|
223
|
+
++branch_factor_; // free a slot but keep waiting for a possible late reply
|
|
224
|
+
}
|
|
225
|
+
if (add_requests(now)) finish();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
o.set(Observer::kFailed);
|
|
230
|
+
if (o.has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
|
|
231
|
+
--branch_factor_;
|
|
232
|
+
if (invoke_count_ > 0) --invoke_count_;
|
|
233
|
+
if (!o.has(Observer::kInitial)) table_.node_failed(o.id(), o.endpoint());
|
|
234
|
+
|
|
235
|
+
if (add_requests(now)) finish();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
void Traversal::traverse(const NodeId& id, const Address& ep) {
|
|
239
|
+
if (done_) return;
|
|
240
|
+
table_.heard_about(id, ep, verify_node_id_for_ip(id, ep.ip));
|
|
241
|
+
add_entry(id, ep);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void Traversal::resort_result(Observer& o) {
|
|
245
|
+
if (done_) return;
|
|
246
|
+
|
|
247
|
+
// Locate the observer by identity. It may have been dropped by a truncation while a
|
|
248
|
+
// reply was in flight, in which case there is nothing to re-place.
|
|
249
|
+
auto it = std::find_if(results_.begin(), results_.end(),
|
|
250
|
+
[&](const ObserverPtr& p) { return p.get() == &o; });
|
|
251
|
+
if (it == results_.end()) return;
|
|
252
|
+
|
|
253
|
+
// If it already sat inside the sorted prefix, account for pulling it out of there.
|
|
254
|
+
// (For a seed it lives in the unsorted tail, so this branch won't fire — but a
|
|
255
|
+
// duplicate id resolved into the prefix could, exactly as in libtorrent.)
|
|
256
|
+
if (it - results_.begin() < sorted_) --sorted_;
|
|
257
|
+
|
|
258
|
+
ObserverPtr ptr = std::move(*it);
|
|
259
|
+
results_.erase(it);
|
|
260
|
+
|
|
261
|
+
// Re-insert by XOR distance now that the id is known — same lower_bound the sorted
|
|
262
|
+
// prefix is built with in add_entry, so the invariant is preserved.
|
|
263
|
+
const NodeId id = ptr->id();
|
|
264
|
+
const auto sorted_end = results_.begin() + sorted_;
|
|
265
|
+
auto pos = std::lower_bound(results_.begin(), sorted_end, id,
|
|
266
|
+
[&](const ObserverPtr& a, const NodeId& nid) { return closer_to(a->id(), nid, target_); });
|
|
267
|
+
results_.insert(pos, std::move(ptr));
|
|
268
|
+
++sorted_;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
void Traversal::finish() {
|
|
272
|
+
if (done_) return;
|
|
273
|
+
done_ = true;
|
|
274
|
+
|
|
275
|
+
// Convergence mechanics, common to every lookup type — *how* it converged, which
|
|
276
|
+
// explains a slow or empty result (e.g. many queried but few alive = a sparse or
|
|
277
|
+
// unresponsive neighbourhood). The peer set and the elapsed time belong to the
|
|
278
|
+
// dht.find layer in node.cpp, so they are deliberately not repeated here.
|
|
279
|
+
int alive = 0, queried = 0;
|
|
280
|
+
for (const auto& o : results_) {
|
|
281
|
+
if (o->has(Observer::kAlive)) ++alive;
|
|
282
|
+
if (o->has(Observer::kQueried)) ++queried;
|
|
283
|
+
}
|
|
284
|
+
LOG_DEBUG("dht.find", name() << " L" << id_ << ' ' << short_hex(target_) << " converged in "
|
|
285
|
+
<< round_ << " round(s): " << alive << " alive / " << queried
|
|
286
|
+
<< " queried, " << results_.size() << " candidate(s)");
|
|
287
|
+
|
|
288
|
+
// We complete as soon as the top-k have answered, but queries to farther nodes may
|
|
289
|
+
// still be in flight (counted in invoke_count_, not in the top-k in_flight).
|
|
290
|
+
for (const auto& obs : results_) {
|
|
291
|
+
Observer* o = obs.get();
|
|
292
|
+
const bool in_flight = o->has(Observer::kQueried) && !o->has(Observer::kAlive) &&
|
|
293
|
+
!o->has(Observer::kFailed) && !o->has(Observer::kDone);
|
|
294
|
+
if (in_flight) {
|
|
295
|
+
o->set(Observer::kDone);
|
|
296
|
+
rpc_.cancel(o);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
on_complete();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
void TraversalObserver::on_response(const KrpcMessage& msg, uint16_t rtt_ms, TimePoint now) {
|
|
304
|
+
if (has(kDone) || has(kAlive)) return; // late/duplicate
|
|
305
|
+
if (algorithm_.finished()) return; // lookup already converged; don't re-enter parse_reply
|
|
306
|
+
set(kAlive); // protect against truncation during traverse()
|
|
307
|
+
|
|
308
|
+
if (has(kInitial) && msg.response_id != NodeId{}) {
|
|
309
|
+
// A seed/router we only knew by address just revealed its real id. Adopt it and
|
|
310
|
+
// re-place this observer among the distance-sorted candidates (a zero id is the
|
|
311
|
+
// "unknown" sentinel, so guard against a malformed reply re-sorting us to the top).
|
|
312
|
+
id_ = msg.response_id;
|
|
313
|
+
algorithm_.resort_result(*this);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
parse_reply(msg);
|
|
317
|
+
|
|
318
|
+
// Adopt the nodes it pointed us at, then report the success (which may finish us).
|
|
319
|
+
for (const auto& kn : msg.nodes)
|
|
320
|
+
algorithm_.traverse(kn.id, Address(kn.ip, kn.port));
|
|
321
|
+
|
|
322
|
+
algorithm_.on_responded(*this, rtt_ms, now);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
} // namespace dht
|
|
326
|
+
} // namespace librats
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file traversal.h
|
|
5
|
+
* @brief Base of an iterative Kademlia lookup (libtorrent's traversal_algorithm).
|
|
6
|
+
*
|
|
7
|
+
* Holds the candidate set as a list of Observers kept sorted by XOR distance to the
|
|
8
|
+
* target, keeps up to branch_factor queries in flight, and converges on the k nodes
|
|
9
|
+
* closest to the target. Each reply feeds discovered nodes back in (and into the
|
|
10
|
+
* routing table); a two-level timeout keeps a slow node from stalling the search.
|
|
11
|
+
*
|
|
12
|
+
* Subclasses supply the three lookup-specific pieces: which query to send (invoke),
|
|
13
|
+
* how to read a reply (the observer subtype via make_observer), and what to do once
|
|
14
|
+
* it converges (on_complete).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "librats/dht/id.h"
|
|
18
|
+
#include "librats/dht/observer.h"
|
|
19
|
+
#include "librats/dht/routing_table.h"
|
|
20
|
+
#include "librats/dht/rpc_manager.h"
|
|
21
|
+
|
|
22
|
+
#include <cstddef>
|
|
23
|
+
#include <cstdint>
|
|
24
|
+
#include <unordered_set>
|
|
25
|
+
#include <vector>
|
|
26
|
+
|
|
27
|
+
namespace librats {
|
|
28
|
+
struct KrpcMessage;
|
|
29
|
+
|
|
30
|
+
namespace dht {
|
|
31
|
+
|
|
32
|
+
class Traversal {
|
|
33
|
+
public:
|
|
34
|
+
Traversal(RoutingTable& table, RpcManager& rpc, const NodeId& self, const NodeId& target);
|
|
35
|
+
virtual ~Traversal();
|
|
36
|
+
|
|
37
|
+
// Seed from the routing table and fire the first round of queries.
|
|
38
|
+
void start(TimePoint now);
|
|
39
|
+
|
|
40
|
+
const NodeId& target() const noexcept { return target_; }
|
|
41
|
+
bool finished() const noexcept { return done_; }
|
|
42
|
+
unsigned id() const noexcept { return id_; } // short per-lookup id for log correlation
|
|
43
|
+
virtual bool is_announce() const noexcept { return false; } // overridden by Announce
|
|
44
|
+
|
|
45
|
+
// Add a bootstrap/router contact whose node id we don't know yet (learned from
|
|
46
|
+
// its reply). Used to seed a lookup when the routing table is empty.
|
|
47
|
+
void add_seed(const Address& ep);
|
|
48
|
+
|
|
49
|
+
// -- driven by observers ----------------------------------------------------
|
|
50
|
+
void on_responded(Observer& o, uint16_t rtt, TimePoint now);
|
|
51
|
+
void on_failed(Observer& o, bool short_timeout, TimePoint now);
|
|
52
|
+
void traverse(const NodeId& id, const Address& ep); // a node a peer told us about
|
|
53
|
+
|
|
54
|
+
// A seed/router whose id we didn't know just told us its real id in a reply. Move it
|
|
55
|
+
// out of the unsorted tail and into its XOR-distance place among the sorted candidates
|
|
56
|
+
// (libtorrent's set_id → resort_result), so a seed close to the target can count
|
|
57
|
+
// toward the k closest and, for an announce, receive an announce_peer.
|
|
58
|
+
void resort_result(Observer& o);
|
|
59
|
+
|
|
60
|
+
protected:
|
|
61
|
+
// Build and send this node's query (returns false if it couldn't be sent).
|
|
62
|
+
virtual bool invoke(const ObserverPtr& o, TimePoint now) = 0;
|
|
63
|
+
virtual ObserverPtr make_observer(const NodeId& id, const Address& ep) = 0;
|
|
64
|
+
virtual void on_complete() {}
|
|
65
|
+
virtual const char* name() const { return "traversal"; }
|
|
66
|
+
|
|
67
|
+
void add_entry(const NodeId& id, const Address& ep, uint8_t flags = 0);
|
|
68
|
+
|
|
69
|
+
RoutingTable& table_;
|
|
70
|
+
RpcManager& rpc_;
|
|
71
|
+
NodeId self_;
|
|
72
|
+
// Candidate observers; the first `sorted_` are ordered by distance to target_.
|
|
73
|
+
std::vector<ObserverPtr> results_;
|
|
74
|
+
|
|
75
|
+
private:
|
|
76
|
+
bool add_requests(TimePoint now); // returns true when the search is complete
|
|
77
|
+
void finish();
|
|
78
|
+
|
|
79
|
+
// Anti-Sybil admission for a candidate's endpoint (libtorrent's dht_restrict_search_ips):
|
|
80
|
+
// record `ep`'s /24 (v4) or /64 (v6) in this lookup's prefix set and return true if that
|
|
81
|
+
// block was already claimed by an earlier candidate (⇒ ignore `ep`). Public IPs only;
|
|
82
|
+
// private/loopback/CGNAT never collide. O(1) amortised, replacing an O(n) scan.
|
|
83
|
+
bool register_subnet(const Address& ep);
|
|
84
|
+
|
|
85
|
+
NodeId target_;
|
|
86
|
+
unsigned id_; // unique lookup id (assigned in ctor), printed as "L<id>" in logs
|
|
87
|
+
int round_ = 0; // how many waves of queries we've fired (the search "round")
|
|
88
|
+
int sorted_ = 0;
|
|
89
|
+
int branch_factor_ = static_cast<int>(kAlpha);
|
|
90
|
+
int invoke_count_ = 0;
|
|
91
|
+
bool done_ = false;
|
|
92
|
+
|
|
93
|
+
// Packed /24 (v4) and /64 (v6) prefixes of candidates already admitted to this lookup,
|
|
94
|
+
// backing register_subnet()'s O(1) Sybil check. Bounded by kMaxResults, so tiny.
|
|
95
|
+
std::unordered_set<uint32_t> subnet4_;
|
|
96
|
+
std::unordered_set<uint64_t> subnet6_;
|
|
97
|
+
|
|
98
|
+
static constexpr std::size_t kMaxResults = 100;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Common observer for nodes inside a traversal: forwards timeouts to the algorithm,
|
|
102
|
+
// and on a reply learns the responder's id, lets the subclass read lookup-specific
|
|
103
|
+
// fields (parse_reply), folds in the nodes it returned, then reports back.
|
|
104
|
+
class TraversalObserver : public Observer {
|
|
105
|
+
public:
|
|
106
|
+
TraversalObserver(Traversal& algorithm, const NodeId& id, const Address& ep)
|
|
107
|
+
: Observer(id, ep), algorithm_(algorithm) {}
|
|
108
|
+
|
|
109
|
+
void on_response(const KrpcMessage& msg, uint16_t rtt_ms, TimePoint now) override;
|
|
110
|
+
void on_timeout(TimePoint now) override { algorithm_.on_failed(*this, /*short=*/false, now); }
|
|
111
|
+
void on_short_timeout(TimePoint now) override { algorithm_.on_failed(*this, /*short=*/true, now); }
|
|
112
|
+
|
|
113
|
+
protected:
|
|
114
|
+
virtual void parse_reply(const KrpcMessage& msg) {} // subclass collects peers/token/etc.
|
|
115
|
+
|
|
116
|
+
Traversal& algorithm_;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
} // namespace dht
|
|
120
|
+
} // namespace librats
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#include "librats/dht/udp_transport.h"
|
|
2
|
+
|
|
3
|
+
namespace librats {
|
|
4
|
+
namespace dht {
|
|
5
|
+
|
|
6
|
+
UdpTransport::UdpTransport(int port, const std::string& bind_address, AddressFamily family)
|
|
7
|
+
: family_(family) {
|
|
8
|
+
init_socket_library(); // idempotent
|
|
9
|
+
|
|
10
|
+
socket_ = create_udp_socket(port, bind_address, family_);
|
|
11
|
+
if (!is_valid_socket(socket_) && port > 0) {
|
|
12
|
+
// A specific port was requested but unavailable — fall back to an ephemeral one.
|
|
13
|
+
// (A negative/invalid port is a hard error, not a fallback case.)
|
|
14
|
+
socket_ = create_udp_socket(0, bind_address, family_);
|
|
15
|
+
}
|
|
16
|
+
if (is_valid_socket(socket_)) {
|
|
17
|
+
port_ = static_cast<uint16_t>(get_bound_port(socket_));
|
|
18
|
+
set_socket_nonblocking(socket_); // recv() drives readiness via its timeout
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
UdpTransport::~UdpTransport() {
|
|
23
|
+
close();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void UdpTransport::send(const Address& to, const std::vector<uint8_t>& datagram) {
|
|
27
|
+
if (!is_open()) return;
|
|
28
|
+
send_udp_data(socket_, datagram, to, family_);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
std::optional<std::vector<uint8_t>> UdpTransport::recv(int timeout_ms, Address& from,
|
|
32
|
+
socket_t interrupt_fd) {
|
|
33
|
+
if (!is_open()) return std::nullopt;
|
|
34
|
+
// 4 KiB comfortably covers any KRPC datagram; an oversized one would be dropped
|
|
35
|
+
// (truncated to WSAEMSGSIZE on Windows), so leave headroom above the ~1500 MTU.
|
|
36
|
+
auto data = receive_udp_data(socket_, 4096, from, timeout_ms, interrupt_fd);
|
|
37
|
+
if (data.empty()) return std::nullopt;
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
void UdpTransport::close() {
|
|
42
|
+
if (is_valid_socket(socket_)) {
|
|
43
|
+
close_socket(socket_);
|
|
44
|
+
socket_ = RATS_INVALID_SOCKET;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} // namespace dht
|
|
49
|
+
} // namespace librats
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file udp_transport.h
|
|
5
|
+
* @brief The real UDP socket behind the DHT's Transport interface.
|
|
6
|
+
*
|
|
7
|
+
* Owns one single-family UDP socket. send() is what dht::Node uses to put datagrams
|
|
8
|
+
* on the wire; recv() is the blocking-with-timeout read the DhtRunner loop pumps.
|
|
9
|
+
* Pure I/O — no DHT logic lives here.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "librats/core/socket.h"
|
|
13
|
+
#include "librats/dht/transport.h"
|
|
14
|
+
|
|
15
|
+
#include <cstdint>
|
|
16
|
+
#include <optional>
|
|
17
|
+
#include <string>
|
|
18
|
+
#include <vector>
|
|
19
|
+
|
|
20
|
+
namespace librats {
|
|
21
|
+
namespace dht {
|
|
22
|
+
|
|
23
|
+
class UdpTransport : public Transport {
|
|
24
|
+
public:
|
|
25
|
+
// Binds a UDP socket on `port` (0 = ephemeral) for `family`. If the requested
|
|
26
|
+
// port is taken it falls back to an ephemeral one. Check is_open() after.
|
|
27
|
+
UdpTransport(int port, const std::string& bind_address, AddressFamily family);
|
|
28
|
+
~UdpTransport() override;
|
|
29
|
+
|
|
30
|
+
bool is_open() const noexcept { return is_valid_socket(socket_); }
|
|
31
|
+
uint16_t port() const noexcept { return port_; } // actual bound port
|
|
32
|
+
|
|
33
|
+
void send(const Address& to, const std::vector<uint8_t>& datagram) override;
|
|
34
|
+
|
|
35
|
+
// Wait up to timeout_ms for a datagram. Returns the payload and fills `from` on
|
|
36
|
+
// success, or nullopt on timeout / error. If `interrupt_fd` is a valid socket it is
|
|
37
|
+
// watched alongside the data socket: when it becomes readable the wait returns early
|
|
38
|
+
// (as nullopt), letting the caller react to posted work without waiting out the timeout.
|
|
39
|
+
std::optional<std::vector<uint8_t>> recv(int timeout_ms, Address& from,
|
|
40
|
+
socket_t interrupt_fd = RATS_INVALID_SOCKET);
|
|
41
|
+
|
|
42
|
+
void close();
|
|
43
|
+
|
|
44
|
+
private:
|
|
45
|
+
socket_t socket_ = RATS_INVALID_SOCKET;
|
|
46
|
+
AddressFamily family_;
|
|
47
|
+
uint16_t port_ = 0;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
} // namespace dht
|
|
51
|
+
} // namespace librats
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file log.h
|
|
5
|
+
* @brief Small logging helpers for the mDNS module.
|
|
6
|
+
*
|
|
7
|
+
* Formatting sugar on top of util/logger.h — nothing more. mDNS logs under the "mdns"
|
|
8
|
+
* tag so it gets its own stable colour and is easy to grep.
|
|
9
|
+
*
|
|
10
|
+
* This lives apart from mdns.h on purpose. mdns.h is reachable from a public header
|
|
11
|
+
* (subsystems/mdns_discovery.h), and util/logger.h defines unprefixed macros —
|
|
12
|
+
* LOG_DEBUG/LOG_INFO/LOG_WARN/LOG_ERROR, plus isatty/fileno on Windows — that would
|
|
13
|
+
* then land in every consumer translation unit. Keep the logging include on the .cpp
|
|
14
|
+
* side, exactly as src/dht/log.h and src/bittorrent/log.h do.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "librats/util/logger.h"
|
|
18
|
+
|
|
19
|
+
#define LOG_MDNS_DEBUG(message) LOG_DEBUG("mdns", message)
|
|
20
|
+
#define LOG_MDNS_INFO(message) LOG_INFO("mdns", message)
|
|
21
|
+
#define LOG_MDNS_WARN(message) LOG_WARN("mdns", message)
|
|
22
|
+
#define LOG_MDNS_ERROR(message) LOG_ERROR("mdns", message)
|