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,241 @@
|
|
|
1
|
+
#include "librats/subsystems/peer_exchange.h"
|
|
2
|
+
#include "librats/node/node_context.h"
|
|
3
|
+
#include "librats/subsystems/hole_punch_service.h"
|
|
4
|
+
#include "librats/util/logger.h"
|
|
5
|
+
#include "librats/util/network_utils.h"
|
|
6
|
+
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <unordered_set>
|
|
9
|
+
|
|
10
|
+
namespace librats {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
constexpr uint8_t kVersion = 1; // peer IPs are raw address bytes (4/16), not text
|
|
15
|
+
constexpr uint8_t kRequest = 0;
|
|
16
|
+
constexpr uint8_t kResponse = 1;
|
|
17
|
+
|
|
18
|
+
constexpr size_t kRequestSize = 4; // ver + op + u16 max
|
|
19
|
+
|
|
20
|
+
void put_u16(Bytes& out, uint16_t v) {
|
|
21
|
+
out.push_back(static_cast<uint8_t>(v >> 8));
|
|
22
|
+
out.push_back(static_cast<uint8_t>(v & 0xFF));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
uint16_t get_u16(const uint8_t* p) {
|
|
26
|
+
return static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | p[1]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
} // namespace
|
|
30
|
+
|
|
31
|
+
PeerExchange::PeerExchange() : PeerExchange(Config()) {}
|
|
32
|
+
PeerExchange::PeerExchange(Config config) : config_(std::move(config)) {}
|
|
33
|
+
PeerExchange::~PeerExchange() { stop(); }
|
|
34
|
+
|
|
35
|
+
void PeerExchange::attach(NodeContext& ctx) {
|
|
36
|
+
network_ = &ctx.network;
|
|
37
|
+
services_ = &ctx.services; // both references outlive every subsystem
|
|
38
|
+
network_->on(MessageType::Pex,
|
|
39
|
+
[this](const Peer& peer, ByteView payload) { handle(peer, payload); });
|
|
40
|
+
network_->on_peer_connected([this](const Peer& peer) { on_connected(peer); });
|
|
41
|
+
network_->on_dial_failed([this](const Address& addr) { on_dial_failed(addr); });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void PeerExchange::start() {
|
|
45
|
+
// Resolved here rather than in attach(): HolePunch may be attached after us, and
|
|
46
|
+
// every attach() runs before any start(). Stored before running_ goes true, so a
|
|
47
|
+
// dial failure arriving on a reactor thread never sees a half-initialised state.
|
|
48
|
+
punch_.store(config_.punch_on_dial_failure && services_
|
|
49
|
+
? services_->get<HolePunchService>()
|
|
50
|
+
: nullptr);
|
|
51
|
+
running_.store(true);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void PeerExchange::stop() {
|
|
55
|
+
running_.store(false);
|
|
56
|
+
punch_.store(nullptr);
|
|
57
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
58
|
+
pending_dials_.clear();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Outgoing request (on connect) ────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
void PeerExchange::on_connected(const Peer& peer) {
|
|
64
|
+
if (!running_.load() || !config_.request_on_connect) return;
|
|
65
|
+
const uint16_t max = static_cast<uint16_t>(std::min<size_t>(config_.request_max, 0xFFFF));
|
|
66
|
+
|
|
67
|
+
uint8_t req[kRequestSize] = {kVersion, kRequest,
|
|
68
|
+
static_cast<uint8_t>(max >> 8), static_cast<uint8_t>(max & 0xFF)};
|
|
69
|
+
network_->send(peer.id(), MessageType::Pex, ByteView(req, kRequestSize));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Inbound dispatch ─────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
void PeerExchange::handle(const Peer& peer, ByteView payload) {
|
|
75
|
+
if (!running_.load() || payload.size() < 2) return;
|
|
76
|
+
if (payload.data()[0] != kVersion) return; // unknown version → ignore
|
|
77
|
+
|
|
78
|
+
const uint8_t op = payload.data()[1];
|
|
79
|
+
const ByteView body(payload.data() + 2, payload.size() - 2);
|
|
80
|
+
if (op == kRequest) {
|
|
81
|
+
if (body.size() < 2) return;
|
|
82
|
+
handle_request(peer, get_u16(body.data()));
|
|
83
|
+
} else if (op == kResponse) {
|
|
84
|
+
handle_response(body);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void PeerExchange::handle_request(const Peer& requester, uint16_t max) {
|
|
89
|
+
const size_t limit = std::min<size_t>(max, config_.max_addresses_per_response);
|
|
90
|
+
if (limit == 0) return;
|
|
91
|
+
const PeerId& self = network_->local_id();
|
|
92
|
+
|
|
93
|
+
Bytes out;
|
|
94
|
+
out.push_back(kVersion);
|
|
95
|
+
out.push_back(kResponse);
|
|
96
|
+
const size_t count_pos = out.size();
|
|
97
|
+
put_u16(out, 0); // count placeholder, back-patched below
|
|
98
|
+
|
|
99
|
+
uint16_t count = 0;
|
|
100
|
+
for (const PeerInfo& p : network_->peers()) {
|
|
101
|
+
if (count >= limit) break;
|
|
102
|
+
if (p.id == requester.id() || p.id == self) continue; // not the asker, not us
|
|
103
|
+
const Address* addr = pick_shareable(p.addresses);
|
|
104
|
+
if (!addr) continue;
|
|
105
|
+
|
|
106
|
+
const ByteView ip = addr->ip.bytes(); // 4 (v4) or 16 (v6) raw bytes
|
|
107
|
+
out.push_back(static_cast<uint8_t>(ip.size()));
|
|
108
|
+
out.insert(out.end(), ip.begin(), ip.end());
|
|
109
|
+
put_u16(out, addr->port);
|
|
110
|
+
const auto& id_bytes = p.id.bytes();
|
|
111
|
+
out.insert(out.end(), id_bytes.begin(), id_bytes.end());
|
|
112
|
+
++count;
|
|
113
|
+
}
|
|
114
|
+
if (count == 0) return; // nothing worth sending
|
|
115
|
+
|
|
116
|
+
out[count_pos] = static_cast<uint8_t>(count >> 8);
|
|
117
|
+
out[count_pos + 1] = static_cast<uint8_t>(count & 0xFF);
|
|
118
|
+
network_->send(requester.id(), MessageType::Pex, ByteView(out));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void PeerExchange::handle_response(ByteView body) {
|
|
122
|
+
const uint8_t* p = body.data();
|
|
123
|
+
size_t n = body.size();
|
|
124
|
+
// Bounds-checked forward reader: any short read aborts the whole response.
|
|
125
|
+
auto take = [&](size_t k) -> const uint8_t* {
|
|
126
|
+
if (n < k) return nullptr;
|
|
127
|
+
const uint8_t* at = p;
|
|
128
|
+
p += k; n -= k;
|
|
129
|
+
return at;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const uint8_t* hdr = take(2);
|
|
133
|
+
if (!hdr) return;
|
|
134
|
+
const uint16_t count = get_u16(hdr);
|
|
135
|
+
|
|
136
|
+
const PeerId self = network_->local_id();
|
|
137
|
+
std::unordered_set<PeerId, PeerId::Hash> connected;
|
|
138
|
+
for (const PeerId& id : network_->connected_peers()) connected.insert(id);
|
|
139
|
+
|
|
140
|
+
// Already at the size the application asked for: read the response no further.
|
|
141
|
+
// The sample is a snapshot anyway — when a peer drops, the next connect asks again.
|
|
142
|
+
if (config_.peer_target && connected.size() >= config_.peer_target) return;
|
|
143
|
+
|
|
144
|
+
size_t dialed = 0;
|
|
145
|
+
for (uint16_t i = 0; i < count; ++i) {
|
|
146
|
+
if (dialed >= config_.max_addresses_per_response) break; // bound receiver work
|
|
147
|
+
|
|
148
|
+
const uint8_t* len_p = take(1);
|
|
149
|
+
if (!len_p) return;
|
|
150
|
+
const uint8_t ip_len = *len_p;
|
|
151
|
+
if (ip_len != 4 && ip_len != 16) return; // malformed
|
|
152
|
+
const uint8_t* ip_p = take(ip_len);
|
|
153
|
+
if (!ip_p) return;
|
|
154
|
+
const auto ip = IpAddress::from_bytes(ByteView(ip_p, ip_len));
|
|
155
|
+
const uint8_t* port_p = take(2);
|
|
156
|
+
if (!port_p) return;
|
|
157
|
+
const uint16_t port = get_u16(port_p);
|
|
158
|
+
const uint8_t* id_p = take(PeerId::kSize);
|
|
159
|
+
if (!id_p) return;
|
|
160
|
+
const auto id = PeerId::from_bytes(ByteView(id_p, PeerId::kSize));
|
|
161
|
+
|
|
162
|
+
if (!id || *id == self || connected.count(*id)) continue; // us / already linked
|
|
163
|
+
if (!ip || port == 0 || ip->is_any()) continue; // not dialable
|
|
164
|
+
Address addr{*ip, port};
|
|
165
|
+
if (!should_dial(addr)) continue; // cooldown / dedup
|
|
166
|
+
|
|
167
|
+
LOG_DEBUG("pex", "Discovered peer " << id->short_hex() << " at " << addr.to_string() << "; dialing");
|
|
168
|
+
remember_dial(addr, *id); // before connect(): the verdict is asynchronous
|
|
169
|
+
network_->connect(addr);
|
|
170
|
+
++dialed;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Punch fallback ───────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
void PeerExchange::on_dial_failed(const Address& address) {
|
|
177
|
+
if (!running_.load()) return;
|
|
178
|
+
HolePunchService* punch = punch_.load();
|
|
179
|
+
if (!punch) return; // no HolePunch attached, or the fallback is off
|
|
180
|
+
|
|
181
|
+
PeerId target;
|
|
182
|
+
{
|
|
183
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
184
|
+
auto it = pending_dials_.find(address);
|
|
185
|
+
// Not one of ours (ReconnectionService and the app dial too), or too old to
|
|
186
|
+
// still be attributable to a peer id.
|
|
187
|
+
if (it == pending_dials_.end()) return;
|
|
188
|
+
const auto age = std::chrono::steady_clock::now() - it->second.started;
|
|
189
|
+
target = it->second.id;
|
|
190
|
+
pending_dials_.erase(it);
|
|
191
|
+
if (age > config_.punch_window) return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
LOG_DEBUG("pex", "Peer " << target.short_hex() << " would not dial at "
|
|
195
|
+
<< address.to_string() << "; asking for a hole punch");
|
|
196
|
+
punch->punch(target);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
void PeerExchange::remember_dial(const Address& addr, const PeerId& id) {
|
|
200
|
+
if (!config_.punch_on_dial_failure) return;
|
|
201
|
+
const auto now = std::chrono::steady_clock::now();
|
|
202
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
203
|
+
|
|
204
|
+
// A dial that neither failed nor was reported leaves its entry behind, so the
|
|
205
|
+
// window is also what bounds the map in steady state; the cap is the backstop.
|
|
206
|
+
for (auto it = pending_dials_.begin(); it != pending_dials_.end();) {
|
|
207
|
+
if (now - it->second.started > config_.punch_window) it = pending_dials_.erase(it);
|
|
208
|
+
else ++it;
|
|
209
|
+
}
|
|
210
|
+
if (pending_dials_.size() >= config_.max_pending_dials) return; // best-effort
|
|
211
|
+
|
|
212
|
+
pending_dials_[addr] = PendingDial{id, now};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
const Address* PeerExchange::pick_shareable(const std::vector<Address>& addrs) const {
|
|
218
|
+
for (const Address& a : addrs) {
|
|
219
|
+
if (a.port == 0 || a.ip.is_any()) continue;
|
|
220
|
+
if (config_.public_only && !network_utils::is_public_ip(a.ip)) continue;
|
|
221
|
+
return &a;
|
|
222
|
+
}
|
|
223
|
+
return nullptr;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
bool PeerExchange::should_dial(const Address& addr) {
|
|
227
|
+
const auto now = std::chrono::steady_clock::now();
|
|
228
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
229
|
+
|
|
230
|
+
// Drop expired entries so the set stays bounded and old failures can be retried.
|
|
231
|
+
for (auto it = recent_dials_.begin(); it != recent_dials_.end();) {
|
|
232
|
+
if (now - it->second > config_.dial_cooldown) it = recent_dials_.erase(it);
|
|
233
|
+
else ++it;
|
|
234
|
+
}
|
|
235
|
+
if (recent_dials_.size() >= config_.max_recent_dials) return false; // overloaded → back off
|
|
236
|
+
|
|
237
|
+
auto [it, inserted] = recent_dials_.emplace(addr, now);
|
|
238
|
+
return inserted; // false ⇒ dialed within the cooldown window
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
} // namespace librats
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file peer_exchange.h
|
|
5
|
+
* @brief Peer exchange (PEX): peers gossip the addresses of peers they know, so a
|
|
6
|
+
* node bootstraps the mesh from its existing links — no DHT/tracker needed.
|
|
7
|
+
*
|
|
8
|
+
* A Subsystem built on PeerNetwork, plus one optional capability: when a peer it
|
|
9
|
+
* discovered refuses to be dialed and HolePunch is present, it asks for a NAT hole
|
|
10
|
+
* punch to that peer's id instead of writing the peer off (see below). This first
|
|
11
|
+
* cut is deliberately
|
|
12
|
+
* **pull-only**: when we connect to a peer we ask it for some of its known peers,
|
|
13
|
+
* and it replies with a random sample (address + id). We then dial the ones we do
|
|
14
|
+
* not already have. Both sides must run PeerExchange — the responder needs it to
|
|
15
|
+
* answer the request.
|
|
16
|
+
*
|
|
17
|
+
* It rides on the node's identify layer: identify is what fills in each peer's
|
|
18
|
+
* dialable address (an inbound peer's listen port is otherwise unknown), and PEX
|
|
19
|
+
* simply forwards those addresses on. A discovered peer we dial becomes an
|
|
20
|
+
* outbound link, so — paired with ReconnectionService — it is then persisted and
|
|
21
|
+
* kept alive automatically.
|
|
22
|
+
*
|
|
23
|
+
* Wire format (MessageType::Pex payload), all integers big-endian:
|
|
24
|
+
* Request : [u8 ver=1][u8 op=0][u16 max]
|
|
25
|
+
* Response : [u8 ver=1][u8 op=1][u16 count] × { [u8 ip_len][ip][u16 port][32B peer_id] }
|
|
26
|
+
*
|
|
27
|
+
* ── Punch fallback ──────────────────────────────────────────────────────────
|
|
28
|
+
* A PEX entry carries an id *and* an address, which is exactly what a peer behind a
|
|
29
|
+
* NAT cannot be reached at: its advertised endpoint is unreachable from the outside,
|
|
30
|
+
* so the dial fails and the mesh silently stays one link short. That is the case
|
|
31
|
+
* HolePunch exists for, and PEX is the only module that holds both halves it needs —
|
|
32
|
+
* so each dial started here is remembered by address for `punch_window`, and if
|
|
33
|
+
* on_dial_failed reports that address back, the id is handed to HolePunchService.
|
|
34
|
+
* Entirely optional: with no HolePunch attached the lookup answers nullptr and the
|
|
35
|
+
* failure is simply the end of the story, as before.
|
|
36
|
+
*
|
|
37
|
+
* Safety: response size is capped both sides; the receiver bounds how many it
|
|
38
|
+
* dials per response and de-duplicates dials with a TTL'd cooldown set (so a slow
|
|
39
|
+
* dial or a repeated response can't trigger a connect storm); malformed payloads
|
|
40
|
+
* are ignored, never fatal. `public_only` restricts sharing to globally-routable
|
|
41
|
+
* addresses for WAN deployments that must not relay private/LAN endpoints. The
|
|
42
|
+
* punch fallback inherits those bounds — one punch per dial we ourselves started,
|
|
43
|
+
* and HolePunch caps sessions and cooldowns on top of that.
|
|
44
|
+
*
|
|
45
|
+
* Threading: handlers run on reactor threads (possibly several with a multi-reactor
|
|
46
|
+
* pool), so the cooldown set is mutex-guarded. The subsystem owns no thread.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
#include "librats/util/rats_export.h"
|
|
50
|
+
#include "librats/node/peer_network.h"
|
|
51
|
+
#include "librats/peer/peer.h"
|
|
52
|
+
#include "librats/peer/peer_id.h"
|
|
53
|
+
#include "librats/core/address.h"
|
|
54
|
+
|
|
55
|
+
#include <atomic>
|
|
56
|
+
#include <chrono>
|
|
57
|
+
#include <cstdint>
|
|
58
|
+
#include <mutex>
|
|
59
|
+
#include <string>
|
|
60
|
+
#include <unordered_map>
|
|
61
|
+
#include <vector>
|
|
62
|
+
|
|
63
|
+
namespace librats {
|
|
64
|
+
|
|
65
|
+
class ServiceRegistry;
|
|
66
|
+
struct HolePunchService;
|
|
67
|
+
|
|
68
|
+
class RATS_API PeerExchange final : public Subsystem {
|
|
69
|
+
public:
|
|
70
|
+
struct Config {
|
|
71
|
+
size_t max_addresses_per_response = 32; ///< cap entries we send / act on per response
|
|
72
|
+
size_t request_max = 32; ///< how many peers we ask for on connect
|
|
73
|
+
bool request_on_connect = true;
|
|
74
|
+
bool public_only = false; ///< only share globally-routable addresses
|
|
75
|
+
std::chrono::milliseconds dial_cooldown{std::chrono::minutes(5)}; ///< re-dial suppression
|
|
76
|
+
size_t max_recent_dials = 4096; ///< bound the cooldown set
|
|
77
|
+
|
|
78
|
+
/// Stop dialing discovered peers once this many are connected. 0 (default)
|
|
79
|
+
/// keeps the historical behaviour: grow without bound. Worth setting,
|
|
80
|
+
/// because NodeConfig::max_peers guards *inbound* connections only — every
|
|
81
|
+
/// discovered peer we dial is honored regardless of it, and PEX is the one
|
|
82
|
+
/// discovery source that compounds (each new peer is asked for more peers).
|
|
83
|
+
/// Answering other peers' requests is unaffected: that costs one message.
|
|
84
|
+
size_t peer_target = 0;
|
|
85
|
+
|
|
86
|
+
/// Ask HolePunch to reach a discovered peer whose address would not dial.
|
|
87
|
+
/// A no-op when no HolePunch is attached.
|
|
88
|
+
bool punch_on_dial_failure = true;
|
|
89
|
+
/// How long a dial started here stays eligible for that fallback. Only has
|
|
90
|
+
/// to outlive the dial itself (connect + handshake, ~15 s worst case).
|
|
91
|
+
std::chrono::milliseconds punch_window{std::chrono::seconds(30)};
|
|
92
|
+
/// Bound on dials awaiting their verdict — one entry is an address and an id.
|
|
93
|
+
size_t max_pending_dials = 512;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
PeerExchange();
|
|
97
|
+
explicit PeerExchange(Config config);
|
|
98
|
+
~PeerExchange() override;
|
|
99
|
+
|
|
100
|
+
void attach(NodeContext& ctx) override;
|
|
101
|
+
void start() override;
|
|
102
|
+
void stop() override;
|
|
103
|
+
|
|
104
|
+
private:
|
|
105
|
+
void on_connected(const Peer& peer);
|
|
106
|
+
void handle(const Peer& peer, ByteView payload);
|
|
107
|
+
void handle_request(const Peer& requester, uint16_t max);
|
|
108
|
+
void handle_response(ByteView body);
|
|
109
|
+
/// An outbound dial resolved as failed. Ours ⇒ punch the peer it was meant for.
|
|
110
|
+
void on_dial_failed(const Address& address);
|
|
111
|
+
|
|
112
|
+
const Address* pick_shareable(const std::vector<Address>& addrs) const;
|
|
113
|
+
bool should_dial(const Address& addr); ///< cooldown + dedup; records on success
|
|
114
|
+
/// Note who a dial we are about to start was aimed at. Called BEFORE connect():
|
|
115
|
+
/// the failure lands on a reactor thread and may beat us back here otherwise.
|
|
116
|
+
void remember_dial(const Address& addr, const PeerId& id);
|
|
117
|
+
|
|
118
|
+
Config config_;
|
|
119
|
+
PeerNetwork* network_ = nullptr;
|
|
120
|
+
ServiceRegistry* services_ = nullptr;
|
|
121
|
+
/// Resolved in start(), cleared in stop(). Atomic because a dial failure can
|
|
122
|
+
/// land on a reactor thread the moment the node is up.
|
|
123
|
+
std::atomic<HolePunchService*> punch_{nullptr};
|
|
124
|
+
std::atomic<bool> running_{false};
|
|
125
|
+
|
|
126
|
+
struct PendingDial {
|
|
127
|
+
PeerId id;
|
|
128
|
+
std::chrono::steady_clock::time_point started;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
std::mutex mutex_;
|
|
132
|
+
std::unordered_map<Address, std::chrono::steady_clock::time_point> recent_dials_;
|
|
133
|
+
std::unordered_map<Address, PendingDial> pending_dials_;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
} // namespace librats
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#include "librats/subsystems/ping_service.h"
|
|
2
|
+
#include "librats/node/node_context.h"
|
|
3
|
+
|
|
4
|
+
#include <cstring>
|
|
5
|
+
|
|
6
|
+
namespace librats {
|
|
7
|
+
|
|
8
|
+
namespace {
|
|
9
|
+
constexpr uint8_t kPing = 0;
|
|
10
|
+
constexpr uint8_t kPong = 1;
|
|
11
|
+
constexpr size_t kProbeSize = 1 + 8; // tag + 8-byte token
|
|
12
|
+
|
|
13
|
+
uint64_t now_ticks() {
|
|
14
|
+
return static_cast<uint64_t>(
|
|
15
|
+
std::chrono::steady_clock::now().time_since_epoch().count());
|
|
16
|
+
}
|
|
17
|
+
} // namespace
|
|
18
|
+
|
|
19
|
+
PingService::PingService(std::chrono::milliseconds interval) : interval_(interval) {}
|
|
20
|
+
|
|
21
|
+
PingService::~PingService() { stop(); }
|
|
22
|
+
|
|
23
|
+
void PingService::attach(NodeContext& ctx) {
|
|
24
|
+
network_ = &ctx.network;
|
|
25
|
+
network_->on(MessageType::Ping,
|
|
26
|
+
[this](const Peer& peer, ByteView payload) { handle(peer, payload); });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void PingService::start() {
|
|
30
|
+
if (running_.exchange(true)) return;
|
|
31
|
+
thread_ = std::thread(&PingService::run, this);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void PingService::stop() {
|
|
35
|
+
if (!running_.exchange(false)) return;
|
|
36
|
+
wake_.notify_all();
|
|
37
|
+
if (thread_.joinable()) thread_.join();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void PingService::run() {
|
|
41
|
+
while (running_.load()) {
|
|
42
|
+
std::unique_lock<std::mutex> lock(wait_mutex_);
|
|
43
|
+
wake_.wait_for(lock, interval_, [this] { return !running_.load(); });
|
|
44
|
+
if (!running_.load()) break;
|
|
45
|
+
lock.unlock();
|
|
46
|
+
ping_all();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void PingService::ping_all() {
|
|
51
|
+
if (!network_) return;
|
|
52
|
+
|
|
53
|
+
uint8_t probe[kProbeSize];
|
|
54
|
+
probe[0] = kPing;
|
|
55
|
+
const uint64_t token = now_ticks();
|
|
56
|
+
std::memcpy(probe + 1, &token, 8);
|
|
57
|
+
|
|
58
|
+
for (const PeerId& id : network_->connected_peers())
|
|
59
|
+
network_->send(id, MessageType::Ping, ByteView(probe, kProbeSize));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void PingService::handle(const Peer& peer, ByteView payload) {
|
|
63
|
+
if (payload.size() != kProbeSize) return;
|
|
64
|
+
const uint8_t tag = payload.data()[0];
|
|
65
|
+
|
|
66
|
+
if (tag == kPing) {
|
|
67
|
+
// Echo the probe straight back as a pong.
|
|
68
|
+
uint8_t pong[kProbeSize];
|
|
69
|
+
pong[0] = kPong;
|
|
70
|
+
std::memcpy(pong + 1, payload.data() + 1, 8);
|
|
71
|
+
network_->send(peer.id(), MessageType::Ping, ByteView(pong, kProbeSize));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (tag == kPong) {
|
|
76
|
+
uint64_t token = 0;
|
|
77
|
+
std::memcpy(&token, payload.data() + 1, 8);
|
|
78
|
+
const uint64_t elapsed_ticks = now_ticks() - token;
|
|
79
|
+
const auto rtt = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
80
|
+
std::chrono::steady_clock::duration(static_cast<std::chrono::steady_clock::rep>(elapsed_ticks)));
|
|
81
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
82
|
+
rtt_[peer.id()] = rtt;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
std::optional<std::chrono::milliseconds> PingService::last_rtt(const PeerId& id) const {
|
|
87
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
88
|
+
auto it = rtt_.find(id);
|
|
89
|
+
if (it == rtt_.end()) return std::nullopt;
|
|
90
|
+
return it->second;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
size_t PingService::alive_peer_count() const {
|
|
94
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
95
|
+
return rtt_.size();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
} // namespace librats
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file ping_service.h
|
|
5
|
+
* @brief Liveness + round-trip-time probing, built purely on PeerNetwork.
|
|
6
|
+
*
|
|
7
|
+
* The first real subsystem on the redesigned plugin model — and the template for
|
|
8
|
+
* porting the larger ones. It depends on NOTHING but the PeerNetwork contract:
|
|
9
|
+
* no Node, no reactor internals, no `friend`. Its own thread periodically pings
|
|
10
|
+
* every connected peer; a peer echoes the probe back, letting the sender measure
|
|
11
|
+
* RTT. Easily mocked by handing it a fake PeerNetwork in tests.
|
|
12
|
+
*
|
|
13
|
+
* Wire format (MessageType::Ping payload): [tag:u8][token:8 bytes].
|
|
14
|
+
* tag 0 = ping, tag 1 = pong. The 8 token bytes are echoed back verbatim, so
|
|
15
|
+
* the original sender decodes them in its own format (endianness irrelevant).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "librats/util/rats_export.h"
|
|
19
|
+
#include "librats/node/peer_network.h"
|
|
20
|
+
#include "librats/peer/peer.h"
|
|
21
|
+
#include "librats/peer/peer_id.h"
|
|
22
|
+
|
|
23
|
+
#include <atomic>
|
|
24
|
+
#include <chrono>
|
|
25
|
+
#include <condition_variable>
|
|
26
|
+
#include <cstdint>
|
|
27
|
+
#include <mutex>
|
|
28
|
+
#include <optional>
|
|
29
|
+
#include <thread>
|
|
30
|
+
#include <unordered_map>
|
|
31
|
+
|
|
32
|
+
namespace librats {
|
|
33
|
+
|
|
34
|
+
class RATS_API PingService final : public Subsystem {
|
|
35
|
+
public:
|
|
36
|
+
explicit PingService(std::chrono::milliseconds interval = std::chrono::seconds(10));
|
|
37
|
+
~PingService() override;
|
|
38
|
+
|
|
39
|
+
void attach(NodeContext& ctx) override;
|
|
40
|
+
void start() override;
|
|
41
|
+
void stop() override;
|
|
42
|
+
|
|
43
|
+
/// Most recent measured round-trip time to a peer, if one has been seen.
|
|
44
|
+
std::optional<std::chrono::milliseconds> last_rtt(const PeerId& id) const;
|
|
45
|
+
|
|
46
|
+
/// Number of peers we have received at least one pong from.
|
|
47
|
+
size_t alive_peer_count() const;
|
|
48
|
+
|
|
49
|
+
private:
|
|
50
|
+
void run(); ///< own thread: ping loop
|
|
51
|
+
void handle(const Peer& peer, ByteView payload);
|
|
52
|
+
void ping_all();
|
|
53
|
+
|
|
54
|
+
PeerNetwork* network_ = nullptr;
|
|
55
|
+
std::chrono::milliseconds interval_;
|
|
56
|
+
|
|
57
|
+
std::thread thread_;
|
|
58
|
+
std::atomic<bool> running_{false};
|
|
59
|
+
std::mutex wait_mutex_;
|
|
60
|
+
std::condition_variable wake_;
|
|
61
|
+
|
|
62
|
+
mutable std::mutex mutex_;
|
|
63
|
+
std::unordered_map<PeerId, std::chrono::milliseconds, PeerId::Hash> rtt_;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
} // namespace librats
|