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,501 @@
|
|
|
1
|
+
#include "librats/dht/dht.h"
|
|
2
|
+
#include "librats/dht/node.h"
|
|
3
|
+
#include "librats/dht/udp_transport.h"
|
|
4
|
+
#include "librats/dht/dht_runner.h"
|
|
5
|
+
#include "librats/dht/persistence.h"
|
|
6
|
+
#include "librats/dht/bep42.h"
|
|
7
|
+
#include "librats/dht/log.h"
|
|
8
|
+
#include "librats/util/network_utils.h"
|
|
9
|
+
#include "librats/util/fs.h"
|
|
10
|
+
|
|
11
|
+
#include <atomic>
|
|
12
|
+
#include <future>
|
|
13
|
+
#include <random>
|
|
14
|
+
#include <shared_mutex>
|
|
15
|
+
#include <utility>
|
|
16
|
+
|
|
17
|
+
namespace librats {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
// How often the running node flushes its routing table to disk, so a crash loses at
|
|
21
|
+
// most this much of the warm contact set (matches the old maintenance-loop cadence).
|
|
22
|
+
constexpr std::chrono::minutes kAutosaveInterval{5};
|
|
23
|
+
|
|
24
|
+
// Serialize a routing-table snapshot to `path`, creating the data dir first. Shared by
|
|
25
|
+
// the explicit save_routing_table() call and the periodic autosave.
|
|
26
|
+
bool write_routing_table(const std::string& path, const std::string& data_dir,
|
|
27
|
+
const NodeId& self, const std::vector<dht::NodeEntry>& contacts) {
|
|
28
|
+
if (!data_dir.empty() && data_dir != ".")
|
|
29
|
+
create_directories(data_dir.c_str());
|
|
30
|
+
return dht::save_routing_table(path, self, contacts);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Marks a DHT loop thread with the Impl it belongs to (null on every other thread).
|
|
34
|
+
// Planted from *inside* the thread at start(), so it needs no synchronisation to read
|
|
35
|
+
// and nothing to clear: it dies with the thread it lives on.
|
|
36
|
+
thread_local const void* tls_loop_owner = nullptr;
|
|
37
|
+
} // namespace
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// The facade owns the engine trio (transport + node + runner) and marshals every
|
|
41
|
+
// public call onto the runner's loop thread, where the lock-free Node lives.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
struct DhtClient::Impl {
|
|
44
|
+
int port = 0;
|
|
45
|
+
std::string bind_address;
|
|
46
|
+
std::string data_directory;
|
|
47
|
+
AddressFamily family = AddressFamily::IPv4;
|
|
48
|
+
bool ipv6 = false;
|
|
49
|
+
NodeId self{}; // authoritative once the node exists; cached otherwise
|
|
50
|
+
std::string external_address; // cached pre-start; node owns it once running
|
|
51
|
+
std::atomic<bool> running{false};
|
|
52
|
+
|
|
53
|
+
std::unique_ptr<dht::UdpTransport> transport;
|
|
54
|
+
std::unique_ptr<dht::Node> node;
|
|
55
|
+
std::unique_ptr<dht::DhtRunner> runner;
|
|
56
|
+
|
|
57
|
+
/// Guards the *existence* of the engine trio above, which start()/stop() create and
|
|
58
|
+
/// destroy while other threads are calling in. `running` alone cannot do that job: a
|
|
59
|
+
/// caller that reads it as true can be descheduled and resume after stop() has already
|
|
60
|
+
/// freed the runner, so the very next `runner->post()` touches a dead object (and a
|
|
61
|
+
/// getter blocked on its promise would never be woken). start()/stop() take it
|
|
62
|
+
/// exclusively for the whole teardown; every public call takes it shared for its whole
|
|
63
|
+
/// duration — via lock_lifetime(), never directly. Teardown therefore cannot begin
|
|
64
|
+
/// while a call is in flight, which is what lets on_loop() below assume the loop
|
|
65
|
+
/// thread is alive and will answer it.
|
|
66
|
+
mutable std::shared_mutex lifetime;
|
|
67
|
+
|
|
68
|
+
/// True when the caller is our own runner's loop thread — see lock_lifetime(). Reads
|
|
69
|
+
/// the marker start() planted on that thread rather than anything hanging off
|
|
70
|
+
/// `runner`, which is precisely what stop() destroys underneath a caller that has
|
|
71
|
+
/// not taken `lifetime` yet.
|
|
72
|
+
bool on_loop_thread() const { return tls_loop_owner == this; }
|
|
73
|
+
|
|
74
|
+
/// `lifetime` held shared for the caller's whole call — except on the loop thread,
|
|
75
|
+
/// where it is deliberately *not* taken. A user callback runs there (peer discovery,
|
|
76
|
+
/// the periodic autosave) and may legitimately call back into the facade; since
|
|
77
|
+
/// stop() takes the lock exclusively and only then joins that very thread, waiting
|
|
78
|
+
/// for it here would deadlock the two against each other forever. Skipping it is
|
|
79
|
+
/// safe for exactly the same reason: if we are executing on the loop, the loop is
|
|
80
|
+
/// alive, so stop() cannot have got past its join() to the resets below it.
|
|
81
|
+
std::shared_lock<std::shared_mutex> lock_lifetime() const {
|
|
82
|
+
if (on_loop_thread()) return {};
|
|
83
|
+
return std::shared_lock<std::shared_mutex>(lifetime);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Run `f` on the loop thread and return its result (used by getters). Safe to block on
|
|
87
|
+
// the promise: callers hold `lifetime` shared, so stop() cannot have started and the
|
|
88
|
+
// loop thread is still there to run the task.
|
|
89
|
+
template <class F>
|
|
90
|
+
auto on_loop(F&& f) -> decltype(f()) {
|
|
91
|
+
// Already on the loop: run it here. Posting would be waiting on ourselves, and
|
|
92
|
+
// `f` touches the same lock-free Node the loop would have touched for us. Only
|
|
93
|
+
// this round-trip path runs inline — the commands below stay post()s, so a
|
|
94
|
+
// callback can never re-enter the Node from inside one of the Node's own
|
|
95
|
+
// callbacks (cancel_lookup erasing the very traversal that is calling us).
|
|
96
|
+
if (on_loop_thread()) return f();
|
|
97
|
+
using R = decltype(f());
|
|
98
|
+
std::promise<R> p;
|
|
99
|
+
auto fut = p.get_future();
|
|
100
|
+
runner->post([&] { p.set_value(f()); }); // cannot fail: `lifetime` is held shared
|
|
101
|
+
return fut.get();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static dht::TimePoint now() { return std::chrono::steady_clock::now(); }
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
DhtClient::DhtClient(int port, const std::string& bind_address,
|
|
108
|
+
const std::string& data_directory, AddressFamily address_family)
|
|
109
|
+
: impl_(std::make_unique<Impl>()) {
|
|
110
|
+
impl_->port = port;
|
|
111
|
+
impl_->bind_address = bind_address;
|
|
112
|
+
impl_->data_directory = data_directory;
|
|
113
|
+
impl_->family = address_family;
|
|
114
|
+
impl_->ipv6 = (address_family == AddressFamily::IPv6);
|
|
115
|
+
|
|
116
|
+
std::mt19937 gen(std::random_device{}());
|
|
117
|
+
std::uniform_int_distribution<int> b(0, 255);
|
|
118
|
+
for (auto& x : impl_->self) x = static_cast<uint8_t>(b(gen));
|
|
119
|
+
|
|
120
|
+
LOG_DEBUG("dht", "client created (" << (impl_->ipv6 ? "IPv6" : "IPv4")
|
|
121
|
+
<< ", port " << impl_->port << ", data_dir '" << impl_->data_directory << "')");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
DhtClient::~DhtClient() { stop(); }
|
|
125
|
+
|
|
126
|
+
bool DhtClient::start() {
|
|
127
|
+
std::unique_lock<std::shared_mutex> lock(impl_->lifetime);
|
|
128
|
+
if (impl_->running.load()) return true;
|
|
129
|
+
|
|
130
|
+
const int requested_port = impl_->port;
|
|
131
|
+
impl_->transport = std::make_unique<dht::UdpTransport>(impl_->port, impl_->bind_address, impl_->family);
|
|
132
|
+
if (!impl_->transport->is_open()) {
|
|
133
|
+
LOG_ERROR("dht", "failed to open " << (impl_->ipv6 ? "IPv6" : "IPv4")
|
|
134
|
+
<< " UDP socket on port " << requested_port);
|
|
135
|
+
impl_->transport.reset();
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
impl_->port = impl_->transport->port(); // record the actual bound port
|
|
139
|
+
if (requested_port > 0 && impl_->port != requested_port)
|
|
140
|
+
LOG_WARN("dht", "port " << requested_port << " unavailable, bound ephemeral port " << impl_->port);
|
|
141
|
+
|
|
142
|
+
// Restore our identity + a warm contact set if we've run here before. Only when a
|
|
143
|
+
// data dir is configured: without one nothing is persisted (stop() saves under the
|
|
144
|
+
// same condition), and since the file name is no longer port-unique, blindly loading
|
|
145
|
+
// a cwd file would cross-contaminate unrelated ephemeral nodes sharing a directory.
|
|
146
|
+
std::vector<dht::NodeEntry> contacts;
|
|
147
|
+
if (!impl_->data_directory.empty()) {
|
|
148
|
+
NodeId loaded = impl_->self;
|
|
149
|
+
if (dht::load_routing_table(routing_table_file_path(), loaded, contacts)) {
|
|
150
|
+
impl_->self = loaded;
|
|
151
|
+
LOG_INFO("dht", "restored identity " << dht::short_hex(impl_->self)
|
|
152
|
+
<< " and " << contacts.size() << " contact(s) from disk");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
impl_->node = std::make_unique<dht::Node>(*impl_->transport, impl_->self, impl_->ipv6);
|
|
157
|
+
if (!contacts.empty()) impl_->node->routing_table().load_contacts(contacts);
|
|
158
|
+
|
|
159
|
+
impl_->runner = std::make_unique<dht::DhtRunner>(*impl_->node, *impl_->transport);
|
|
160
|
+
|
|
161
|
+
// Periodically persist the warm contact set so a crash doesn't lose it (we otherwise
|
|
162
|
+
// only save on a clean stop()). Runs on the loop thread, so it reads the lock-free
|
|
163
|
+
// Node directly — no on_loop() round-trip and no lock needed (see lock_lifetime()):
|
|
164
|
+
// stop() joins the loop before it resets the node, so the node is alive for as long
|
|
165
|
+
// as this can run. Only when a data dir is configured, to avoid littering the CWD.
|
|
166
|
+
if (!impl_->data_directory.empty() && impl_->data_directory != ".") {
|
|
167
|
+
impl_->runner->set_periodic(kAutosaveInterval, [this] {
|
|
168
|
+
if (!impl_->node) return;
|
|
169
|
+
const auto contacts = impl_->node->routing_table().good_contacts();
|
|
170
|
+
write_routing_table(routing_table_file_path(), impl_->data_directory,
|
|
171
|
+
impl_->node->self(), contacts);
|
|
172
|
+
LOG_DEBUG("dht", "autosaved " << contacts.size() << " contact(s)");
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Mark the loop thread before anything runs on it, so a callback that calls back
|
|
177
|
+
// into the facade is recognised from its very first invocation.
|
|
178
|
+
impl_->runner->start([impl = impl_.get()] { tls_loop_owner = impl; });
|
|
179
|
+
impl_->running.store(true);
|
|
180
|
+
LOG_INFO("dht", "started, node " << dht::short_hex(impl_->self) << ", "
|
|
181
|
+
<< (impl_->ipv6 ? "IPv6" : "IPv4") << " on port " << impl_->port);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
void DhtClient::stop() {
|
|
186
|
+
// Exclusive for the whole teardown: no public call may be mid-flight while the trio
|
|
187
|
+
// below is destroyed, and none may start until it is done. See Impl::lifetime. This
|
|
188
|
+
// one method still may not be called *from* the loop thread — it joins that thread,
|
|
189
|
+
// so it would be waiting on itself no matter how the lock behaved.
|
|
190
|
+
std::unique_lock<std::shared_mutex> lock(impl_->lifetime);
|
|
191
|
+
if (!impl_->running.exchange(false)) return;
|
|
192
|
+
LOG_INFO("dht", "stopping");
|
|
193
|
+
if (impl_->runner) impl_->runner->stop(); // join the loop thread first
|
|
194
|
+
|
|
195
|
+
// Single-threaded again: persist (only when a data dir is configured, to avoid
|
|
196
|
+
// littering) directly from the idle node. Written inline rather than through
|
|
197
|
+
// save_routing_table(), which would take `lifetime` a second time and deadlock.
|
|
198
|
+
if (impl_->node && !impl_->data_directory.empty()) {
|
|
199
|
+
const auto contacts = impl_->node->routing_table().good_contacts();
|
|
200
|
+
write_routing_table(routing_table_file_path(), impl_->data_directory,
|
|
201
|
+
impl_->node->self(), contacts);
|
|
202
|
+
LOG_INFO("dht", "saved " << contacts.size() << " contact(s) to disk");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
impl_->runner.reset();
|
|
206
|
+
impl_->node.reset();
|
|
207
|
+
impl_->transport.reset();
|
|
208
|
+
LOG_INFO("dht", "stopped");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
void DhtClient::shutdown_immediate() { stop(); }
|
|
212
|
+
|
|
213
|
+
bool DhtClient::is_running() const { return impl_->running.load(); }
|
|
214
|
+
|
|
215
|
+
uint16_t DhtClient::get_port() const {
|
|
216
|
+
const auto lock = impl_->lock_lifetime();
|
|
217
|
+
return impl_->transport ? impl_->transport->port() : 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
bool DhtClient::bootstrap(const std::vector<HostEndpoint>& bootstrap_nodes) {
|
|
221
|
+
const auto lock = impl_->lock_lifetime();
|
|
222
|
+
if (!impl_->running.load()) {
|
|
223
|
+
LOG_WARN("dht", "bootstrap ignored — client not running");
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
// Resolve hostnames (and filter by family) before the seeds reach the engine: it
|
|
227
|
+
// matches a reply's source address verbatim against the address it queried, so a
|
|
228
|
+
// seed left as a hostname would have every reply dropped as a spoof. Resolution can
|
|
229
|
+
// block, so do it here on the caller's thread, not on the DHT loop.
|
|
230
|
+
auto resolved = resolve_bootstrap_nodes(bootstrap_nodes, impl_->ipv6);
|
|
231
|
+
if (resolved.empty()) {
|
|
232
|
+
LOG_WARN("dht", "bootstrap failed — no usable " << (impl_->ipv6 ? "IPv6" : "IPv4")
|
|
233
|
+
<< " node(s) among " << bootstrap_nodes.size() << " seed(s)");
|
|
234
|
+
return false; // nothing usable for our family
|
|
235
|
+
}
|
|
236
|
+
LOG_INFO("dht", "bootstrapping via " << resolved.size() << " seed(s)");
|
|
237
|
+
impl_->runner->post([this, resolved] {
|
|
238
|
+
impl_->node->set_bootstrap_nodes(resolved); // reused by spider reseed
|
|
239
|
+
impl_->node->bootstrap(resolved, Impl::now());
|
|
240
|
+
});
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
bool DhtClient::find_peers(const InfoHash& info_hash, PeerDiscoveryCallback callback) {
|
|
245
|
+
const auto lock = impl_->lock_lifetime();
|
|
246
|
+
if (!impl_->running.load()) return false;
|
|
247
|
+
// A user callback runs on the loop thread, so a throw would take the whole DHT
|
|
248
|
+
// down — isolate it here.
|
|
249
|
+
auto safe = [](PeerDiscoveryCallback cb, const std::vector<Address>& p, const InfoHash& h) {
|
|
250
|
+
if (!cb) return;
|
|
251
|
+
try { cb(p, h); } catch (...) {}
|
|
252
|
+
};
|
|
253
|
+
impl_->runner->post([this, info_hash, callback, safe] {
|
|
254
|
+
// Dedup: callers (dht_discovery / bt_client) re-issue find_peers on a refresh
|
|
255
|
+
// timer that fires far faster than a lookup completes (~25s in the wild), so
|
|
256
|
+
// without this they pile up into many concurrent identical traversals hammering
|
|
257
|
+
// the same nodes. If a search for this hash is already running, drop the
|
|
258
|
+
// duplicate — the in-flight lookup keeps delivering peers to its subscriber.
|
|
259
|
+
if (impl_->node->lookup_active(info_hash, /*announce=*/false)) {
|
|
260
|
+
LOG_DEBUG("dht", "find_peers " << dht::short_hex(info_hash)
|
|
261
|
+
<< " skipped — search already active");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
impl_->node->find_peers(
|
|
265
|
+
info_hash,
|
|
266
|
+
[callback, info_hash, safe](const std::vector<Address>& p) { safe(callback, p, info_hash); },
|
|
267
|
+
[callback, info_hash, safe](const std::vector<Address>& all) { safe(callback, all, info_hash); },
|
|
268
|
+
Impl::now());
|
|
269
|
+
});
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
bool DhtClient::announce_peer(const InfoHash& info_hash, uint16_t port, PeerDiscoveryCallback callback) {
|
|
274
|
+
const auto lock = impl_->lock_lifetime();
|
|
275
|
+
if (!impl_->running.load()) return false;
|
|
276
|
+
const uint16_t dht_port = impl_->transport->port();
|
|
277
|
+
const uint16_t announce_port = port == 0 ? dht_port : port;
|
|
278
|
+
const bool implied = (announce_port == dht_port);
|
|
279
|
+
// A user callback runs on the loop thread, so a throw would take the whole DHT down —
|
|
280
|
+
// isolate it (same guard as find_peers).
|
|
281
|
+
auto safe = [](PeerDiscoveryCallback cb, const std::vector<Address>& p, const InfoHash& h) {
|
|
282
|
+
if (!cb) return;
|
|
283
|
+
try { cb(p, h); } catch (...) {}
|
|
284
|
+
};
|
|
285
|
+
impl_->runner->post([this, info_hash, announce_port, implied, callback, safe] {
|
|
286
|
+
// Dedup, same rationale as find_peers: don't stack a second announce traversal
|
|
287
|
+
// for a hash that's still announcing (one announce ran ~28s in the wild).
|
|
288
|
+
if (impl_->node->lookup_active(info_hash, /*announce=*/true)) {
|
|
289
|
+
LOG_DEBUG("dht", "announce " << dht::short_hex(info_hash)
|
|
290
|
+
<< " skipped — announce already active");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
impl_->node->announce_peer(
|
|
294
|
+
info_hash, announce_port, implied,
|
|
295
|
+
// Peers stream in as the announce traversal discovers them, then once more in
|
|
296
|
+
// full on completion — so a caller that announces also gets peer discovery for
|
|
297
|
+
// free, with no separate find_peers needed.
|
|
298
|
+
[callback, info_hash, safe](const std::vector<Address>& p) { safe(callback, p, info_hash); },
|
|
299
|
+
[callback, info_hash, safe](const std::vector<Address>& all) { safe(callback, all, info_hash); },
|
|
300
|
+
Impl::now());
|
|
301
|
+
});
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
void DhtClient::cancel_search(const InfoHash& info_hash) {
|
|
306
|
+
const auto lock = impl_->lock_lifetime();
|
|
307
|
+
if (!impl_->running.load()) return;
|
|
308
|
+
impl_->runner->post([this, info_hash] { impl_->node->cancel_lookup(info_hash); });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
NodeId DhtClient::get_node_id() const {
|
|
312
|
+
const auto lock = impl_->lock_lifetime();
|
|
313
|
+
if (impl_->running.load()) return impl_->on_loop([this] { return impl_->node->self(); });
|
|
314
|
+
return impl_->self;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
void DhtClient::set_external_ip(const std::string& ip_str) {
|
|
318
|
+
const auto lock = impl_->lock_lifetime();
|
|
319
|
+
// STUN and the public API hand us a textual IP — parse once here, then the engine
|
|
320
|
+
// works purely in numeric IpAddress terms (no repeated re-parsing per BEP 42 step).
|
|
321
|
+
const auto ip = IpAddress::parse(ip_str);
|
|
322
|
+
if (!ip) return;
|
|
323
|
+
|
|
324
|
+
if (impl_->running.load()) {
|
|
325
|
+
impl_->runner->post([this, addr = *ip] { impl_->node->set_external_ip(addr); });
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
// Before start: apply BEP 42 to the cached identity so get_node_id() reflects it
|
|
329
|
+
// and the node adopts it at start.
|
|
330
|
+
if (!dht::is_public_address(*ip)) return;
|
|
331
|
+
if (ip->is_v6() != impl_->ipv6) return;
|
|
332
|
+
impl_->external_address = ip_str;
|
|
333
|
+
if (dht::verify_node_id_for_ip(impl_->self, *ip)) return;
|
|
334
|
+
std::mt19937 gen(std::random_device{}());
|
|
335
|
+
NodeId regenerated;
|
|
336
|
+
if (dht::generate_node_id_from_ip(*ip, regenerated, gen)) {
|
|
337
|
+
impl_->self = regenerated;
|
|
338
|
+
LOG_INFO("dht", "external IP " << ip_str << " set before start, node id → "
|
|
339
|
+
<< dht::short_hex(impl_->self));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
std::string DhtClient::get_external_address() const {
|
|
344
|
+
const auto lock = impl_->lock_lifetime();
|
|
345
|
+
if (impl_->running.load())
|
|
346
|
+
return impl_->on_loop([this] { return impl_->node->external_address().to_string(); });
|
|
347
|
+
return impl_->external_address;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
bool DhtClient::verify_node_id_for_ip(const NodeId& id, const std::string& ip) {
|
|
351
|
+
return dht::verify_node_id_for_ip(id, ip);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
std::vector<HostEndpoint> DhtClient::get_default_bootstrap_nodes() {
|
|
355
|
+
return dht::Node::default_bootstrap_nodes();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
size_t DhtClient::get_routing_table_size() const {
|
|
359
|
+
const auto lock = impl_->lock_lifetime();
|
|
360
|
+
if (!impl_->running.load()) return 0;
|
|
361
|
+
return impl_->on_loop([this] { return impl_->node->routing_table().size(); });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
bool DhtClient::is_search_active(const InfoHash& info_hash) const {
|
|
366
|
+
const auto lock = impl_->lock_lifetime();
|
|
367
|
+
if (!impl_->running.load()) return false;
|
|
368
|
+
return impl_->on_loop([this, info_hash] { return impl_->node->lookup_active(info_hash, false); });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
bool DhtClient::is_announce_active(const InfoHash& info_hash) const {
|
|
372
|
+
const auto lock = impl_->lock_lifetime();
|
|
373
|
+
if (!impl_->running.load()) return false;
|
|
374
|
+
return impl_->on_loop([this, info_hash] { return impl_->node->lookup_active(info_hash, true); });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
size_t DhtClient::get_active_searches_count() const {
|
|
378
|
+
const auto lock = impl_->lock_lifetime();
|
|
379
|
+
if (!impl_->running.load()) return 0;
|
|
380
|
+
return impl_->on_loop([this] { return impl_->node->lookup_count(false); });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
size_t DhtClient::get_active_announces_count() const {
|
|
384
|
+
const auto lock = impl_->lock_lifetime();
|
|
385
|
+
if (!impl_->running.load()) return 0;
|
|
386
|
+
return impl_->on_loop([this] { return impl_->node->lookup_count(true); });
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
AddressFamily DhtClient::address_family() const { return impl_->family; }
|
|
390
|
+
bool DhtClient::is_ipv6() const { return impl_->ipv6; }
|
|
391
|
+
|
|
392
|
+
std::string DhtClient::routing_table_file_path() const {
|
|
393
|
+
// One routing-table file per data dir (plus the IPv6 variant). The name is
|
|
394
|
+
// deliberately port-independent: an ephemeral (port 0) node binds a different port
|
|
395
|
+
// each run, so a port in the name would mean it never finds its own saved identity
|
|
396
|
+
// + warm set on restart.
|
|
397
|
+
const char* suffix = impl_->ipv6 ? "_v6" : "";
|
|
398
|
+
const std::string name = std::string("dht_routing") + suffix + ".json";
|
|
399
|
+
if (!impl_->data_directory.empty() && impl_->data_directory != ".")
|
|
400
|
+
return impl_->data_directory + "/" + name;
|
|
401
|
+
return name;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
bool DhtClient::save_routing_table() {
|
|
405
|
+
const auto lock = impl_->lock_lifetime();
|
|
406
|
+
NodeId self = impl_->self;
|
|
407
|
+
std::vector<dht::NodeEntry> contacts;
|
|
408
|
+
if (impl_->running.load() && impl_->node) {
|
|
409
|
+
auto snap = impl_->on_loop([this] {
|
|
410
|
+
return std::make_pair(impl_->node->self(), impl_->node->routing_table().good_contacts());
|
|
411
|
+
});
|
|
412
|
+
self = snap.first;
|
|
413
|
+
contacts = std::move(snap.second);
|
|
414
|
+
} else if (impl_->node) {
|
|
415
|
+
self = impl_->node->self();
|
|
416
|
+
contacts = impl_->node->routing_table().good_contacts();
|
|
417
|
+
}
|
|
418
|
+
return write_routing_table(routing_table_file_path(), impl_->data_directory, self, contacts);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
bool DhtClient::load_routing_table() {
|
|
422
|
+
const auto lock = impl_->lock_lifetime();
|
|
423
|
+
NodeId loaded = impl_->self;
|
|
424
|
+
std::vector<dht::NodeEntry> contacts;
|
|
425
|
+
if (!dht::load_routing_table(routing_table_file_path(), loaded, contacts)) return false;
|
|
426
|
+
impl_->self = loaded;
|
|
427
|
+
if (impl_->running.load() && impl_->node)
|
|
428
|
+
impl_->on_loop([this, &contacts] { impl_->node->routing_table().load_contacts(contacts); return 0; });
|
|
429
|
+
LOG_DEBUG("dht", "loaded " << contacts.size() << " contact(s) from disk");
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
void DhtClient::set_data_directory(const std::string& directory) { impl_->data_directory = directory; }
|
|
434
|
+
|
|
435
|
+
#ifdef RATS_SEARCH_FEATURES
|
|
436
|
+
void DhtClient::set_spider_mode(bool enable) {
|
|
437
|
+
const auto lock = impl_->lock_lifetime();
|
|
438
|
+
if (impl_->running.load()) impl_->node->set_spider_mode(enable); // atomic flag, loop-safe
|
|
439
|
+
}
|
|
440
|
+
bool DhtClient::is_spider_mode() const {
|
|
441
|
+
const auto lock = impl_->lock_lifetime();
|
|
442
|
+
return impl_->running.load() && impl_->node->is_spider_mode(); // atomic read, loop-safe
|
|
443
|
+
}
|
|
444
|
+
void DhtClient::set_spider_announce_callback(SpiderAnnounceCallback callback) {
|
|
445
|
+
const auto lock = impl_->lock_lifetime();
|
|
446
|
+
if (impl_->running.load())
|
|
447
|
+
impl_->runner->post([this, callback]() mutable { impl_->node->set_spider_announce_callback(std::move(callback)); });
|
|
448
|
+
}
|
|
449
|
+
void DhtClient::set_spider_ignore(bool ignore) {
|
|
450
|
+
const auto lock = impl_->lock_lifetime();
|
|
451
|
+
if (impl_->running.load()) impl_->node->set_spider_ignore(ignore); // atomic
|
|
452
|
+
}
|
|
453
|
+
bool DhtClient::is_spider_ignoring() const {
|
|
454
|
+
const auto lock = impl_->lock_lifetime();
|
|
455
|
+
return impl_->running.load() && impl_->node->is_spider_ignoring();
|
|
456
|
+
}
|
|
457
|
+
void DhtClient::spider_walk() {
|
|
458
|
+
const auto lock = impl_->lock_lifetime();
|
|
459
|
+
if (impl_->running.load()) impl_->runner->post([this] { impl_->node->spider_walk(Impl::now()); });
|
|
460
|
+
}
|
|
461
|
+
size_t DhtClient::get_spider_pool_size() const {
|
|
462
|
+
const auto lock = impl_->lock_lifetime();
|
|
463
|
+
if (!impl_->running.load()) return 0;
|
|
464
|
+
return impl_->on_loop([this] { return impl_->node->spider_pool_size(); });
|
|
465
|
+
}
|
|
466
|
+
size_t DhtClient::get_spider_visited_count() const {
|
|
467
|
+
const auto lock = impl_->lock_lifetime();
|
|
468
|
+
if (!impl_->running.load()) return 0;
|
|
469
|
+
return impl_->on_loop([this] { return impl_->node->spider_visited_count(); });
|
|
470
|
+
}
|
|
471
|
+
void DhtClient::clear_spider_state() {
|
|
472
|
+
const auto lock = impl_->lock_lifetime();
|
|
473
|
+
if (impl_->running.load()) impl_->runner->post([this] { impl_->node->clear_spider_state(); });
|
|
474
|
+
}
|
|
475
|
+
#endif // RATS_SEARCH_FEATURES
|
|
476
|
+
|
|
477
|
+
std::vector<Address> resolve_bootstrap_nodes(const std::vector<HostEndpoint>& nodes, bool ipv6) {
|
|
478
|
+
std::vector<Address> out;
|
|
479
|
+
out.reserve(nodes.size());
|
|
480
|
+
for (const auto& n : nodes) {
|
|
481
|
+
if (n.host.empty() || n.port == 0) continue;
|
|
482
|
+
|
|
483
|
+
// Numeric literal: keep it only if it belongs to this node's family.
|
|
484
|
+
if (auto ip = IpAddress::parse(n.host)) {
|
|
485
|
+
if (ip->is_v6() == ipv6) out.push_back(Address{*ip, n.port});
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Hostname: resolve to our family (A for IPv4, AAAA for IPv6). Drop it if it
|
|
490
|
+
// has no record there, rather than feed the engine an unmatchable hostname.
|
|
491
|
+
const std::string ip = ipv6 ? network_utils::resolve_hostname_v6(n.host)
|
|
492
|
+
: network_utils::resolve_hostname(n.host);
|
|
493
|
+
if (auto a = IpAddress::parse(ip)) out.push_back(Address{*a, n.port});
|
|
494
|
+
}
|
|
495
|
+
return out;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ---- node id -> hex (for logging) ------------------------------------------
|
|
499
|
+
std::string node_id_to_hex(const NodeId& id) { return dht::to_hex(id); }
|
|
500
|
+
|
|
501
|
+
} // namespace librats
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file dht.h
|
|
5
|
+
* @brief Public DHT client — a thin facade over the modular dht::Node engine.
|
|
6
|
+
*
|
|
7
|
+
* This is the long-standing `librats::DhtClient` API every consumer already uses. It
|
|
8
|
+
* now wraps the rewritten engine (src/dht/node.h + friends): a single-threaded,
|
|
9
|
+
* lock-free dht::Node driven by a DhtRunner over a UdpTransport. The facade owns that
|
|
10
|
+
* trio and marshals public calls onto the runner's thread, so callers keep the same
|
|
11
|
+
* simple, thread-safe surface they always had.
|
|
12
|
+
*
|
|
13
|
+
* The engine internals live in namespace librats::dht; this header only exposes the
|
|
14
|
+
* stable public types so consumers (and their includes) are unchanged.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "librats/util/rats_export.h"
|
|
18
|
+
#include "librats/core/address.h"
|
|
19
|
+
#include "librats/core/host_endpoint.h"
|
|
20
|
+
#include "librats/core/socket.h" // AddressFamily
|
|
21
|
+
#include "librats/dht/krpc.h" // KrpcProtocol/KrpcMessage (wire format, used by tests)
|
|
22
|
+
|
|
23
|
+
#include <array>
|
|
24
|
+
#include <chrono>
|
|
25
|
+
#include <cstdint>
|
|
26
|
+
#include <functional>
|
|
27
|
+
#include <memory>
|
|
28
|
+
#include <random>
|
|
29
|
+
#include <string>
|
|
30
|
+
#include <vector>
|
|
31
|
+
|
|
32
|
+
namespace librats {
|
|
33
|
+
|
|
34
|
+
// Standard BitTorrent DHT port. The public NodeId/InfoHash id types come from
|
|
35
|
+
// dht/krpc.h (included above), so the facade re-exposes them without duplicating
|
|
36
|
+
// the engine-internal dht::NodeId.
|
|
37
|
+
constexpr int DHT_PORT = 6881;
|
|
38
|
+
|
|
39
|
+
/// Discovered peers for an info-hash are delivered through this callback.
|
|
40
|
+
using PeerDiscoveryCallback = std::function<void(const std::vector<Address>& peers, const InfoHash& info_hash)>;
|
|
41
|
+
|
|
42
|
+
#ifdef RATS_SEARCH_FEATURES
|
|
43
|
+
/// Spider mode: invoked when a peer announces it has a torrent.
|
|
44
|
+
using SpiderAnnounceCallback = std::function<void(const InfoHash& info_hash, const Address& peer)>;
|
|
45
|
+
#endif
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Kademlia DHT client (BEP 5/32/42). One instance serves one address family.
|
|
49
|
+
*/
|
|
50
|
+
class RATS_API DhtClient {
|
|
51
|
+
public:
|
|
52
|
+
DhtClient(int port = DHT_PORT, const std::string& bind_address = "",
|
|
53
|
+
const std::string& data_directory = "",
|
|
54
|
+
AddressFamily address_family = AddressFamily::IPv4);
|
|
55
|
+
~DhtClient();
|
|
56
|
+
|
|
57
|
+
DhtClient(const DhtClient&) = delete;
|
|
58
|
+
DhtClient& operator=(const DhtClient&) = delete;
|
|
59
|
+
|
|
60
|
+
bool start();
|
|
61
|
+
void stop();
|
|
62
|
+
void shutdown_immediate();
|
|
63
|
+
bool is_running() const;
|
|
64
|
+
uint16_t get_port() const;
|
|
65
|
+
|
|
66
|
+
bool bootstrap(const std::vector<HostEndpoint>& bootstrap_nodes);
|
|
67
|
+
bool find_peers(const InfoHash& info_hash, PeerDiscoveryCallback callback);
|
|
68
|
+
bool announce_peer(const InfoHash& info_hash, uint16_t port = 0, PeerDiscoveryCallback callback = nullptr);
|
|
69
|
+
void cancel_search(const InfoHash& info_hash);
|
|
70
|
+
|
|
71
|
+
NodeId get_node_id() const;
|
|
72
|
+
void set_external_ip(const std::string& ip);
|
|
73
|
+
std::string get_external_address() const;
|
|
74
|
+
static bool verify_node_id_for_ip(const NodeId& id, const std::string& ip);
|
|
75
|
+
static std::vector<HostEndpoint> get_default_bootstrap_nodes();
|
|
76
|
+
|
|
77
|
+
size_t get_routing_table_size() const;
|
|
78
|
+
bool is_search_active(const InfoHash& info_hash) const;
|
|
79
|
+
bool is_announce_active(const InfoHash& info_hash) const;
|
|
80
|
+
size_t get_active_searches_count() const;
|
|
81
|
+
size_t get_active_announces_count() const;
|
|
82
|
+
|
|
83
|
+
AddressFamily address_family() const;
|
|
84
|
+
bool is_ipv6() const;
|
|
85
|
+
|
|
86
|
+
std::string routing_table_file_path() const;
|
|
87
|
+
bool save_routing_table();
|
|
88
|
+
bool load_routing_table();
|
|
89
|
+
void set_data_directory(const std::string& directory);
|
|
90
|
+
|
|
91
|
+
#ifdef RATS_SEARCH_FEATURES
|
|
92
|
+
void set_spider_mode(bool enable);
|
|
93
|
+
bool is_spider_mode() const;
|
|
94
|
+
void set_spider_announce_callback(SpiderAnnounceCallback callback);
|
|
95
|
+
void set_spider_ignore(bool ignore);
|
|
96
|
+
bool is_spider_ignoring() const;
|
|
97
|
+
void spider_walk();
|
|
98
|
+
size_t get_spider_pool_size() const;
|
|
99
|
+
size_t get_spider_visited_count() const;
|
|
100
|
+
void clear_spider_state();
|
|
101
|
+
#endif
|
|
102
|
+
|
|
103
|
+
private:
|
|
104
|
+
struct Impl;
|
|
105
|
+
std::unique_ptr<Impl> impl_;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Resolve any hostname bootstrap entries to numeric IPs of the given family, dropping
|
|
109
|
+
// wrong-family and unresolvable ones. The DHT engine matches a reply's source address
|
|
110
|
+
// verbatim against the address it queried (anti-spoofing), so a seed kept as a hostname
|
|
111
|
+
// would have every reply dropped — seeds must enter the engine as numeric IPs of the
|
|
112
|
+
// right family. Resolution can block, so callers run this off the DHT loop thread.
|
|
113
|
+
// Exposed for testing.
|
|
114
|
+
std::vector<Address> resolve_bootstrap_nodes(const std::vector<HostEndpoint>& nodes, bool ipv6);
|
|
115
|
+
|
|
116
|
+
// Node id -> lowercase hex, for logging.
|
|
117
|
+
std::string node_id_to_hex(const NodeId& id);
|
|
118
|
+
|
|
119
|
+
} // namespace librats
|