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,264 @@
|
|
|
1
|
+
#include "librats/node/dialer.h"
|
|
2
|
+
#include "librats/util/logger.h"
|
|
3
|
+
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <utility>
|
|
6
|
+
|
|
7
|
+
namespace librats {
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
|
|
11
|
+
/// The PeerTransports bit standing for one transport.
|
|
12
|
+
uint8_t mask_for(TransportKind kind) noexcept {
|
|
13
|
+
return kind == TransportKind::Udp ? PeerTransportUdp : PeerTransportTcp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
} // namespace
|
|
17
|
+
|
|
18
|
+
std::vector<TransportKind> Dialer::transport_order(uint8_t known) const {
|
|
19
|
+
const TransportKind first = policy_.preferred;
|
|
20
|
+
const TransportKind second = first == TransportKind::Udp ? TransportKind::Tcp
|
|
21
|
+
: TransportKind::Udp;
|
|
22
|
+
const auto allowed = [this](TransportKind k) {
|
|
23
|
+
return k == TransportKind::Udp ? policy_.allow_udp : policy_.allow_tcp;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
std::vector<TransportKind> order;
|
|
27
|
+
if (allowed(first)) order.push_back(first);
|
|
28
|
+
// A zero fallback delay means "the preferred transport or nothing" — the
|
|
29
|
+
// second entry is exactly what the fallback timer would bring in.
|
|
30
|
+
if (allowed(second) && policy_.fallback_delay.count() > 0) order.push_back(second);
|
|
31
|
+
// ...unless the preferred one is not available at all, in which case the other
|
|
32
|
+
// is not a fallback, it is the only way out.
|
|
33
|
+
if (order.empty() && allowed(second)) order.push_back(second);
|
|
34
|
+
|
|
35
|
+
// A peer at this address has already told us which wires it accepts. Bring
|
|
36
|
+
// those to the front — stably, so the policy still decides between two the
|
|
37
|
+
// peer accepts equally. Nothing is dropped: the knowledge can be stale, and
|
|
38
|
+
// the cost of acting on a stale entry must stay "one fallback delay", never
|
|
39
|
+
// "this target is undialable". A single-entry order is left alone, which is
|
|
40
|
+
// what keeps a zero fallback delay meaning exactly what it says.
|
|
41
|
+
if (known != PeerTransportNone && order.size() > 1) {
|
|
42
|
+
std::stable_partition(order.begin(), order.end(), [known](TransportKind k) {
|
|
43
|
+
return (known & mask_for(k)) != 0;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return order;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void Dialer::dial(const std::string& host, uint16_t port) {
|
|
50
|
+
auto target = std::make_shared<Target>();
|
|
51
|
+
target->host = host;
|
|
52
|
+
target->port = port;
|
|
53
|
+
|
|
54
|
+
bool started = false;
|
|
55
|
+
bool empty = false;
|
|
56
|
+
{
|
|
57
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
58
|
+
if (stopped_) return;
|
|
59
|
+
// Under the lock: the order depends on what identify has taught us about
|
|
60
|
+
// this address, which lives behind the same mutex as the bookkeeping.
|
|
61
|
+
target->pending = transport_order(recall(host, port));
|
|
62
|
+
empty = target->pending.empty();
|
|
63
|
+
if (!empty) started = start_next(target);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (empty) LOG_WARN("dialer", "No transport enabled; cannot dial " << host << ":" << port);
|
|
67
|
+
|
|
68
|
+
// Never with the lock held: a failure handler may well dial again (that is
|
|
69
|
+
// what a reconnection policy does), and this mutex is not recursive.
|
|
70
|
+
if (!started) on_failed_(host, port);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── What identify taught us about an address ────────────────────────────────
|
|
74
|
+
|
|
75
|
+
std::string Dialer::key_for(const std::string& host, uint16_t port) {
|
|
76
|
+
return host + ":" + std::to_string(port);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
uint8_t Dialer::recall(const std::string& host, uint16_t port) const {
|
|
80
|
+
const auto it = known_.find(key_for(host, port));
|
|
81
|
+
if (it == known_.end()) return PeerTransportNone;
|
|
82
|
+
if (Clock::now() - it->second.learned_at > kMemoryTtl) return PeerTransportNone;
|
|
83
|
+
return it->second.transports;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void Dialer::evict_one(Clock::time_point now) {
|
|
87
|
+
// One pass, and only when the cache is full: drop everything that has expired,
|
|
88
|
+
// and note the oldest survivor in case nothing had. A map capped at
|
|
89
|
+
// kMaxRemembered makes this bounded work on a path that runs once per dial.
|
|
90
|
+
auto oldest = known_.end();
|
|
91
|
+
for (auto it = known_.begin(); it != known_.end();) {
|
|
92
|
+
if (now - it->second.learned_at > kMemoryTtl) {
|
|
93
|
+
it = known_.erase(it);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (oldest == known_.end() || it->second.learned_at < oldest->second.learned_at)
|
|
97
|
+
oldest = it;
|
|
98
|
+
++it;
|
|
99
|
+
}
|
|
100
|
+
if (known_.size() >= kMaxRemembered && oldest != known_.end()) known_.erase(oldest);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void Dialer::remember(const std::string& host, uint16_t port, uint8_t transports) {
|
|
104
|
+
// 0 is "the peer did not say" (an older node predating the field), not
|
|
105
|
+
// "supports nothing" — storing it would teach us the opposite of the truth.
|
|
106
|
+
if (transports == PeerTransportNone || port == 0 || host.empty()) return;
|
|
107
|
+
|
|
108
|
+
const auto now = Clock::now();
|
|
109
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
110
|
+
|
|
111
|
+
std::string key = key_for(host, port);
|
|
112
|
+
const auto it = known_.find(key);
|
|
113
|
+
if (it != known_.end()) {
|
|
114
|
+
it->second.transports = transports;
|
|
115
|
+
it->second.learned_at = now;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (known_.size() >= kMaxRemembered) evict_one(now);
|
|
120
|
+
known_.emplace(std::move(key), Known{transports, now});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
uint8_t Dialer::known_transports(const std::string& host, uint16_t port) const {
|
|
124
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
125
|
+
return recall(host, port);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
std::vector<TransportKind> Dialer::planned_order(const std::string& host, uint16_t port) const {
|
|
129
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
130
|
+
return transport_order(recall(host, port));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
bool Dialer::start_next(const std::shared_ptr<Target>& target) {
|
|
134
|
+
// Walk the queue rather than taking only its head: a transport the pool cannot
|
|
135
|
+
// provide (no UDP socket, say) never becomes an attempt, so the next one takes
|
|
136
|
+
// its place at once instead of waiting out a fallback delay for nothing.
|
|
137
|
+
while (!target->pending.empty()) {
|
|
138
|
+
const TransportKind kind = target->pending.front();
|
|
139
|
+
target->pending.erase(target->pending.begin());
|
|
140
|
+
|
|
141
|
+
Reactor& reactor = reactors_.pick(kind);
|
|
142
|
+
const ConnId id = reactor.connect(target->host, target->port, kind);
|
|
143
|
+
if (id == kInvalidConnId) continue;
|
|
144
|
+
|
|
145
|
+
const PeerRoute route{reactor.index(), id};
|
|
146
|
+
target->routes.push_back(route);
|
|
147
|
+
attempts_.emplace(Key{route.reactor, route.conn}, target);
|
|
148
|
+
LOG_DEBUG("dialer", "Dialing " << target->host << ":" << target->port
|
|
149
|
+
<< " over " << to_string(kind));
|
|
150
|
+
|
|
151
|
+
if (!target->pending.empty()) arm_fallback(target, reactor);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
void Dialer::arm_fallback(const std::shared_ptr<Target>& target, Reactor& reactor) {
|
|
158
|
+
// The timer holds only a weak reference and re-checks the target when it
|
|
159
|
+
// fires, so there is nothing to cancel when the race is decided early: the
|
|
160
|
+
// target is either gone or marked won, and the timer quietly does nothing.
|
|
161
|
+
std::weak_ptr<Target> weak = target;
|
|
162
|
+
const auto delay = policy_.fallback_delay;
|
|
163
|
+
Reactor* r = &reactor;
|
|
164
|
+
|
|
165
|
+
r->execute([this, r, weak, delay] {
|
|
166
|
+
r->schedule(delay, [this, weak] {
|
|
167
|
+
auto target = weak.lock();
|
|
168
|
+
if (!target) return;
|
|
169
|
+
|
|
170
|
+
bool failed = false;
|
|
171
|
+
std::string host;
|
|
172
|
+
uint16_t port = 0;
|
|
173
|
+
{
|
|
174
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
175
|
+
if (stopped_ || target->won || target->pending.empty()) return;
|
|
176
|
+
LOG_DEBUG("dialer", "Racing the fallback transport for "
|
|
177
|
+
<< target->host << ":" << target->port);
|
|
178
|
+
if (!start_next(target) && target->routes.empty()) {
|
|
179
|
+
failed = true;
|
|
180
|
+
host = target->host;
|
|
181
|
+
port = target->port;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (failed) on_failed_(host, port);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void Dialer::on_established(PeerRoute route) {
|
|
190
|
+
std::vector<PeerRoute> superseded;
|
|
191
|
+
{
|
|
192
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
193
|
+
auto it = attempts_.find(Key{route.reactor, route.conn});
|
|
194
|
+
if (it == attempts_.end()) return; // inbound, or a dial already settled
|
|
195
|
+
|
|
196
|
+
std::shared_ptr<Target> target = it->second;
|
|
197
|
+
target->won = true;
|
|
198
|
+
target->pending.clear();
|
|
199
|
+
|
|
200
|
+
for (const PeerRoute& other : target->routes) {
|
|
201
|
+
attempts_.erase(Key{other.reactor, other.conn});
|
|
202
|
+
if (other != route) superseded.push_back(other);
|
|
203
|
+
}
|
|
204
|
+
target->routes.clear();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Outside the lock: cancel_dial() posts to a reactor, and on this thread it may
|
|
208
|
+
// run the teardown — and therefore on_closed() — straight away.
|
|
209
|
+
//
|
|
210
|
+
// cancel_dial rather than close: a sibling that has finished its own handshake
|
|
211
|
+
// in the meantime is no longer this race's to end. Ending it anyway would be
|
|
212
|
+
// one end deciding, alone, which of two links to a peer lives — and the other
|
|
213
|
+
// end, which never saw this race, can just as easily decide the other way, so
|
|
214
|
+
// both links die and the peer is lost. Left alone, it reaches the peer table as
|
|
215
|
+
// an ordinary duplicate and is resolved there, the same way at both ends.
|
|
216
|
+
for (const PeerRoute& other : superseded)
|
|
217
|
+
reactors_.by_index(other.reactor).cancel_dial(other.conn, CloseReason::DialSuperseded);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void Dialer::on_closed(PeerRoute route) {
|
|
221
|
+
bool failed = false;
|
|
222
|
+
std::string host;
|
|
223
|
+
uint16_t port = 0;
|
|
224
|
+
{
|
|
225
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
226
|
+
auto it = attempts_.find(Key{route.reactor, route.conn});
|
|
227
|
+
if (it == attempts_.end()) return;
|
|
228
|
+
|
|
229
|
+
std::shared_ptr<Target> target = it->second;
|
|
230
|
+
attempts_.erase(it);
|
|
231
|
+
target->routes.erase(std::remove(target->routes.begin(), target->routes.end(), route),
|
|
232
|
+
target->routes.end());
|
|
233
|
+
if (stopped_ || target->won) return;
|
|
234
|
+
|
|
235
|
+
// This attempt is out. Bring the next transport in now rather than waiting
|
|
236
|
+
// for the fallback timer: a transport that fails fast should not cost the
|
|
237
|
+
// full delay before the other one is even tried.
|
|
238
|
+
if (!start_next(target) && target->routes.empty()) {
|
|
239
|
+
failed = true;
|
|
240
|
+
host = target->host;
|
|
241
|
+
port = target->port;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (failed) on_failed_(host, port);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
void Dialer::start() {
|
|
248
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
249
|
+
stopped_ = false;
|
|
250
|
+
attempts_.clear(); // nothing from a previous run survives a restart
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void Dialer::stop() {
|
|
254
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
255
|
+
stopped_ = true;
|
|
256
|
+
attempts_.clear();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
size_t Dialer::in_flight() const {
|
|
260
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
261
|
+
return attempts_.size();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
} // namespace librats
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file dialer.h
|
|
5
|
+
* @brief Picks the transport for an outbound connection, and races the fallback.
|
|
6
|
+
*
|
|
7
|
+
* With two equal transports available, "connect to this address" stops being one
|
|
8
|
+
* action. UDP is the better first choice for a peer-to-peer node — one socket and
|
|
9
|
+
* one NAT mapping for every peer, and a source port that is actually dialable —
|
|
10
|
+
* but some networks block or throttle it, and finding that out by waiting for a
|
|
11
|
+
* dial to time out would be unacceptable. So a dial is a small race, in the
|
|
12
|
+
* spirit of Happy Eyeballs (RFC 8305):
|
|
13
|
+
*
|
|
14
|
+
* t = 0 start the preferred transport
|
|
15
|
+
* t = fallback_delay if it has not established yet, start the other one too
|
|
16
|
+
* first to establish wins; attempts still in flight for this target are cancelled
|
|
17
|
+
* all attempts failed report the dial as failed, exactly once
|
|
18
|
+
*
|
|
19
|
+
* Racing rather than falling back sequentially matters: on a UDP-hostile network
|
|
20
|
+
* the cost is the fallback delay, not a full connect timeout, and on a normal
|
|
21
|
+
* network the second attempt is never made at all.
|
|
22
|
+
*
|
|
23
|
+
* ── Not paying for the same discovery twice ─────────────────────────────────
|
|
24
|
+
* A peer says in its identify message which wires it accepts. Racing is how a
|
|
25
|
+
* dial finds that out when it has nothing to go on; paying the fallback delay
|
|
26
|
+
* *again*, on every later dial to an address that already told us, is pure loss —
|
|
27
|
+
* and a reconnection policy redials the same handful of addresses for the life of
|
|
28
|
+
* the node. So the dialer keeps a small cache of address → transports (fed by
|
|
29
|
+
* Node::handle_identify) and starts with a transport the peer actually has.
|
|
30
|
+
*
|
|
31
|
+
* Two properties make that safe to act on:
|
|
32
|
+
* - It reorders, it never removes. Knowledge can be stale — a peer restarts
|
|
33
|
+
* with a different config — and a stale entry must cost a fallback delay,
|
|
34
|
+
* never a target that cannot be dialed at all. The transport the peer did not
|
|
35
|
+
* claim stays in the queue behind the one it did.
|
|
36
|
+
* - Only addresses the node can vouch for are recorded (see the caller). A
|
|
37
|
+
* peer's self-advertised address list is its unverified word about where
|
|
38
|
+
* anyone lives, so it is not allowed to set the dial order for someone else.
|
|
39
|
+
*
|
|
40
|
+
* Cancelling the loser is the reason Reactor::connect hands back a ConnId
|
|
41
|
+
* synchronously — without it every race would cost a second connection that has to
|
|
42
|
+
* be taken down after the fact.
|
|
43
|
+
*
|
|
44
|
+
* ── What this decides, and what it must not ─────────────────────────────────
|
|
45
|
+
* The race decides which transport this node *dials on*, and nothing more. It is
|
|
46
|
+
* settled by timing, here — and the peer never saw a race at all, only connections
|
|
47
|
+
* arriving. So a verdict reached here is one the other end cannot reproduce: if it
|
|
48
|
+
* also decided which link lives, it could just as well keep the one this end
|
|
49
|
+
* dropped, and then each end has torn down the other's choice and the peer is gone.
|
|
50
|
+
*
|
|
51
|
+
* Hence cancel_dial() rather than close(): an attempt that has already finished its
|
|
52
|
+
* handshake is left alone and handed on as an ordinary duplicate, for PeerTable::add
|
|
53
|
+
* to resolve from what both ends see alike (see peer_table.cpp).
|
|
54
|
+
*
|
|
55
|
+
* Threading: dial() is called from any thread; the attempt callbacks arrive on
|
|
56
|
+
* reactor threads. One mutex guards the (small) bookkeeping; nothing on the data
|
|
57
|
+
* path goes through here.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
#include "librats/core/types.h"
|
|
61
|
+
#include "librats/peer/peer_table.h" // PeerRoute
|
|
62
|
+
#include "librats/transport/reactor_pool.h"
|
|
63
|
+
|
|
64
|
+
#include <chrono>
|
|
65
|
+
#include <cstdint>
|
|
66
|
+
#include <functional>
|
|
67
|
+
#include <map>
|
|
68
|
+
#include <memory>
|
|
69
|
+
#include <mutex>
|
|
70
|
+
#include <string>
|
|
71
|
+
#include <unordered_map>
|
|
72
|
+
#include <vector>
|
|
73
|
+
|
|
74
|
+
namespace librats {
|
|
75
|
+
|
|
76
|
+
struct DialPolicy {
|
|
77
|
+
TransportKind preferred = TransportKind::Udp;
|
|
78
|
+
bool allow_tcp = true;
|
|
79
|
+
bool allow_udp = true;
|
|
80
|
+
std::chrono::milliseconds fallback_delay{1200};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
class Dialer {
|
|
84
|
+
public:
|
|
85
|
+
using Clock = std::chrono::steady_clock;
|
|
86
|
+
|
|
87
|
+
/// Called once per target when every attempt has failed. Reactor thread.
|
|
88
|
+
using FailureHandler = std::function<void(const std::string& host, uint16_t port)>;
|
|
89
|
+
|
|
90
|
+
/// Addresses the transports cache holds. A node meets far more addresses than
|
|
91
|
+
/// it will ever redial (DHT and PEX see thousands), so this is a cache with a
|
|
92
|
+
/// ceiling, not a directory: past it the least recently learned entry goes.
|
|
93
|
+
static constexpr size_t kMaxRemembered = 1024;
|
|
94
|
+
|
|
95
|
+
/// How long a learned transports mask is trusted. A peer may restart on a
|
|
96
|
+
/// different config, and an entry that outlives the truth would send every
|
|
97
|
+
/// dial to a wire the peer no longer has — which costs the very fallback delay
|
|
98
|
+
/// this cache exists to avoid.
|
|
99
|
+
static constexpr std::chrono::minutes kMemoryTtl{10};
|
|
100
|
+
|
|
101
|
+
Dialer(ReactorPool& reactors, DialPolicy policy, FailureHandler on_failed)
|
|
102
|
+
: reactors_(reactors), policy_(policy), on_failed_(std::move(on_failed)) {}
|
|
103
|
+
|
|
104
|
+
/// Begin dialing `host:port`. Non-blocking; the outcome surfaces through the
|
|
105
|
+
/// node's peer-connected / dial-failed events.
|
|
106
|
+
void dial(const std::string& host, uint16_t port);
|
|
107
|
+
|
|
108
|
+
// — attempt outcomes, reported by the Node (reactor thread) —
|
|
109
|
+
|
|
110
|
+
/// An attempt completed its handshake; it wins the target.
|
|
111
|
+
void on_established(PeerRoute route);
|
|
112
|
+
/// An attempt ended (failed, was superseded, or simply closed later).
|
|
113
|
+
void on_closed(PeerRoute route);
|
|
114
|
+
|
|
115
|
+
/// Record which transports the peer reachable at `host:port` said it accepts
|
|
116
|
+
/// (a PeerTransports bitmask from its identify message). A later dial to the
|
|
117
|
+
/// same address starts with one of them instead of spending the fallback delay
|
|
118
|
+
/// rediscovering it. A mask of 0 ("did not say") is ignored rather than stored
|
|
119
|
+
/// as "supports nothing". Thread-safe; called from a reactor thread.
|
|
120
|
+
void remember(const std::string& host, uint16_t port, uint8_t transports);
|
|
121
|
+
|
|
122
|
+
/// What is currently believed about `host:port`, 0 if nothing (or if what was
|
|
123
|
+
/// learned has expired). Diagnostics and tests.
|
|
124
|
+
uint8_t known_transports(const std::string& host, uint16_t port) const;
|
|
125
|
+
|
|
126
|
+
/// The transports a dial to `host:port` would try, in order — exactly what
|
|
127
|
+
/// dial() computes, without starting anything. Diagnostics and tests.
|
|
128
|
+
std::vector<TransportKind> planned_order(const std::string& host, uint16_t port) const;
|
|
129
|
+
|
|
130
|
+
/// Accept dials again after a stop() — a Node may be restarted. What identify
|
|
131
|
+
/// taught us about addresses is knowledge about the network rather than
|
|
132
|
+
/// in-flight bookkeeping, so it deliberately survives the cycle.
|
|
133
|
+
void start();
|
|
134
|
+
|
|
135
|
+
/// Drop all bookkeeping; no further dials or fallbacks are started.
|
|
136
|
+
void stop();
|
|
137
|
+
|
|
138
|
+
/// Attempts currently in flight (diagnostics/tests).
|
|
139
|
+
size_t in_flight() const;
|
|
140
|
+
|
|
141
|
+
private:
|
|
142
|
+
struct Target {
|
|
143
|
+
std::string host;
|
|
144
|
+
uint16_t port = 0;
|
|
145
|
+
std::vector<TransportKind> pending; ///< transports not yet tried, in order
|
|
146
|
+
std::vector<PeerRoute> routes; ///< attempts currently in flight
|
|
147
|
+
bool won = false;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
using Key = std::pair<uint8_t, ConnId>; ///< (reactor, conn) — unique per attempt
|
|
151
|
+
|
|
152
|
+
/// What one address last told us about itself.
|
|
153
|
+
struct Known {
|
|
154
|
+
uint8_t transports = 0;
|
|
155
|
+
Clock::time_point learned_at{};
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/// Start the next available transport in `target`'s queue; false if the queue
|
|
159
|
+
/// is exhausted (nothing was started). Caller holds the mutex — and must
|
|
160
|
+
/// report a resulting failure only after releasing it.
|
|
161
|
+
bool start_next(const std::shared_ptr<Target>& target);
|
|
162
|
+
/// Arm the timer that brings the second transport in. Caller holds the mutex.
|
|
163
|
+
void arm_fallback(const std::shared_ptr<Target>& target, Reactor& reactor);
|
|
164
|
+
|
|
165
|
+
/// The transports to try, in order: what the policy allows, with anything the
|
|
166
|
+
/// peer at this address said it accepts (`known`, 0 for "no idea") moved to
|
|
167
|
+
/// the front. Reorders only — see the note at the top of this file.
|
|
168
|
+
std::vector<TransportKind> transport_order(uint8_t known) const;
|
|
169
|
+
|
|
170
|
+
/// What is known about `host:port`, 0 if nothing or if it has expired. Caller
|
|
171
|
+
/// holds the mutex.
|
|
172
|
+
uint8_t recall(const std::string& host, uint16_t port) const;
|
|
173
|
+
/// Make room for one entry in a full cache. Caller holds the mutex.
|
|
174
|
+
void evict_one(Clock::time_point now);
|
|
175
|
+
|
|
176
|
+
static std::string key_for(const std::string& host, uint16_t port);
|
|
177
|
+
|
|
178
|
+
ReactorPool& reactors_;
|
|
179
|
+
DialPolicy policy_;
|
|
180
|
+
FailureHandler on_failed_;
|
|
181
|
+
|
|
182
|
+
mutable std::mutex mutex_;
|
|
183
|
+
std::map<Key, std::shared_ptr<Target>> attempts_;
|
|
184
|
+
std::unordered_map<std::string, Known> known_; ///< address → transports it accepts
|
|
185
|
+
bool stopped_ = false;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
} // namespace librats
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file host_events.h
|
|
5
|
+
* @brief Node-level event types published on the EventBus.
|
|
6
|
+
*
|
|
7
|
+
* These describe things that happen to the *host* the node runs on (as opposed to
|
|
8
|
+
* the peer mesh), which several subsystems may want to react to independently.
|
|
9
|
+
* They are plain value types; a subsystem subscribes via `ctx.events.on<...>()`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
namespace librats {
|
|
16
|
+
|
|
17
|
+
/// The set of local interface addresses changed — an interface came up/down, an
|
|
18
|
+
/// IP was added/removed, the default route flipped (Wi-Fi↔cellular, VPN, dock,
|
|
19
|
+
/// wake-from-sleep). Long-lived nodes react by renewing port mappings and
|
|
20
|
+
/// re-discovering/re-announcing their public endpoint, which would otherwise go
|
|
21
|
+
/// stale. Emitted (debounced) by the Node's NetworkMonitor.
|
|
22
|
+
struct NetworkChanged {
|
|
23
|
+
std::vector<std::string> local_addresses; ///< new, full list of local IPs
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
} // namespace librats
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#include "librats/node/identify.h"
|
|
2
|
+
|
|
3
|
+
#include <utility>
|
|
4
|
+
|
|
5
|
+
namespace librats {
|
|
6
|
+
|
|
7
|
+
namespace {
|
|
8
|
+
|
|
9
|
+
void put_u16(Bytes& out, uint16_t v) {
|
|
10
|
+
out.push_back(static_cast<uint8_t>(v >> 8));
|
|
11
|
+
out.push_back(static_cast<uint8_t>(v & 0xFF));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
void put_addr(Bytes& out, const Address& a) {
|
|
15
|
+
// [len:u8][ip bytes: 4 or 16][port:u16] — the IP goes out as raw address bytes,
|
|
16
|
+
// not text, so an endpoint costs 7 (v4) or 19 (v6) bytes on the wire.
|
|
17
|
+
const ByteView ip = a.ip.bytes();
|
|
18
|
+
out.push_back(static_cast<uint8_t>(ip.size()));
|
|
19
|
+
out.insert(out.end(), ip.begin(), ip.end());
|
|
20
|
+
put_u16(out, a.port);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
bool emittable(const Address& a) {
|
|
24
|
+
return !a.ip.is_unspecified(); // size() is then 4 or 16, always within kMaxIpLength
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Bounds-checked forward reader: every read is validated against the remaining
|
|
28
|
+
/// length, and the first short read latches `ok` false so the rest are no-ops.
|
|
29
|
+
struct Reader {
|
|
30
|
+
const uint8_t* p;
|
|
31
|
+
size_t n;
|
|
32
|
+
bool ok = true;
|
|
33
|
+
|
|
34
|
+
explicit Reader(ByteView v) : p(v.data()), n(v.size()) {}
|
|
35
|
+
|
|
36
|
+
uint8_t u8() {
|
|
37
|
+
if (n < 1) { ok = false; return 0; }
|
|
38
|
+
const uint8_t v = *p;
|
|
39
|
+
++p; --n;
|
|
40
|
+
return v;
|
|
41
|
+
}
|
|
42
|
+
uint16_t u16() {
|
|
43
|
+
const uint8_t hi = u8();
|
|
44
|
+
const uint8_t lo = u8();
|
|
45
|
+
return static_cast<uint16_t>((hi << 8) | lo);
|
|
46
|
+
}
|
|
47
|
+
std::string str(size_t len) {
|
|
48
|
+
if (n < len) { ok = false; return {}; }
|
|
49
|
+
std::string s(reinterpret_cast<const char*>(p), len);
|
|
50
|
+
p += len; n -= len;
|
|
51
|
+
return s;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
} // namespace
|
|
56
|
+
|
|
57
|
+
Bytes IdentifyMessage::encode() const {
|
|
58
|
+
Bytes out;
|
|
59
|
+
out.push_back(kVersion);
|
|
60
|
+
put_u16(out, listen_port);
|
|
61
|
+
|
|
62
|
+
// Emit at most kMaxAddresses well-formed addresses; the count prefix matches.
|
|
63
|
+
Bytes addr_blob;
|
|
64
|
+
uint8_t emitted = 0;
|
|
65
|
+
for (const Address& a : addresses) {
|
|
66
|
+
if (emitted >= kMaxAddresses) break;
|
|
67
|
+
if (!emittable(a) || a.port == 0) continue;
|
|
68
|
+
put_addr(addr_blob, a);
|
|
69
|
+
++emitted;
|
|
70
|
+
}
|
|
71
|
+
out.push_back(emitted);
|
|
72
|
+
out.insert(out.end(), addr_blob.begin(), addr_blob.end());
|
|
73
|
+
|
|
74
|
+
// Observed address: an IP length of 0 means "none".
|
|
75
|
+
if (observed && emittable(*observed)) {
|
|
76
|
+
put_addr(out, *observed);
|
|
77
|
+
} else {
|
|
78
|
+
out.push_back(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Appended last, where a decoder that predates it just ignores it.
|
|
82
|
+
out.push_back(transports);
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
std::optional<IdentifyMessage> IdentifyMessage::decode(ByteView in) {
|
|
87
|
+
Reader r(in);
|
|
88
|
+
|
|
89
|
+
const uint8_t version = r.u8();
|
|
90
|
+
if (!r.ok || version != kVersion) return std::nullopt; // unknown/old → "no identify"
|
|
91
|
+
|
|
92
|
+
IdentifyMessage msg;
|
|
93
|
+
msg.listen_port = r.u16();
|
|
94
|
+
|
|
95
|
+
const uint8_t count = r.u8();
|
|
96
|
+
if (!r.ok || count > kMaxAddresses) return std::nullopt; // capped: never over-reserve
|
|
97
|
+
msg.addresses.reserve(count);
|
|
98
|
+
for (uint8_t i = 0; i < count; ++i) {
|
|
99
|
+
const uint8_t ip_len = r.u8();
|
|
100
|
+
if (!r.ok || (ip_len != 4 && ip_len != 16)) return std::nullopt;
|
|
101
|
+
std::string ip = r.str(ip_len);
|
|
102
|
+
const uint16_t port = r.u16();
|
|
103
|
+
if (!r.ok) return std::nullopt;
|
|
104
|
+
auto addr = IpAddress::from_bytes(ByteView(ip));
|
|
105
|
+
if (!addr) return std::nullopt;
|
|
106
|
+
msg.addresses.push_back(Address{*addr, port});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const uint8_t obs_len = r.u8();
|
|
110
|
+
if (!r.ok) return std::nullopt;
|
|
111
|
+
if (obs_len != 0) {
|
|
112
|
+
if (obs_len != 4 && obs_len != 16) return std::nullopt;
|
|
113
|
+
std::string ip = r.str(obs_len);
|
|
114
|
+
const uint16_t port = r.u16();
|
|
115
|
+
if (!r.ok) return std::nullopt;
|
|
116
|
+
auto addr = IpAddress::from_bytes(ByteView(ip));
|
|
117
|
+
if (!addr) return std::nullopt;
|
|
118
|
+
msg.observed = Address{*addr, port};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Optional trailing extension. A sender that predates it leaves the field at
|
|
122
|
+
// 0, which reads as "did not say" rather than "supports nothing".
|
|
123
|
+
const uint8_t transports = r.u8();
|
|
124
|
+
if (r.ok) msg.transports = transports;
|
|
125
|
+
|
|
126
|
+
// Any further trailing bytes (a future extension) are tolerated and ignored.
|
|
127
|
+
return msg;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
} // namespace librats
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file identify.h
|
|
5
|
+
* @brief The node's "identify" control message — how peers learn each other's
|
|
6
|
+
* dialable addresses (libp2p-identify in spirit).
|
|
7
|
+
*
|
|
8
|
+
* A TCP socket only reveals a peer's *source* endpoint: its IP plus an ephemeral
|
|
9
|
+
* port the OS picked for the outbound socket — never the port it listens on. So
|
|
10
|
+
* the dialable address of an INBOUND peer is unknowable from the connection
|
|
11
|
+
* alone; the peer must tell us. Right after the secure handshake, each side sends
|
|
12
|
+
* an Identify message over the authenticated channel carrying:
|
|
13
|
+
*
|
|
14
|
+
* - listen_port : the TCP port this node accepts connections on. The receiver
|
|
15
|
+
* pairs it with the IP it sees the sender at to form a dialable
|
|
16
|
+
* address (the key that makes inbound peers reconnectable).
|
|
17
|
+
* - addresses : additional self-advertised dialable addresses (e.g. each local
|
|
18
|
+
* interface IP with the listen port) — the "multiaddr set".
|
|
19
|
+
* - observed : the address the sender observed the RECIPIENT connecting from,
|
|
20
|
+
* so a node can learn its own public IP as peers see it.
|
|
21
|
+
* - transports : which wires the sender accepts on that port (TCP, the
|
|
22
|
+
* datagram transport, or both). Both bind the SAME port, so
|
|
23
|
+
* this is a property of the node rather than of an address —
|
|
24
|
+
* it tells a peer which transports are worth dialing before it
|
|
25
|
+
* spends a fallback delay finding out.
|
|
26
|
+
*
|
|
27
|
+
* The transports byte is appended after the older fields, and decode has always
|
|
28
|
+
* ignored trailing bytes, so a node that predates it simply reads the message
|
|
29
|
+
* without it and reports "unspecified" rather than failing.
|
|
30
|
+
*
|
|
31
|
+
* The wire form is a compact, versioned, fully bounds-checked binary blob — decode
|
|
32
|
+
* never trusts a length without checking it against the buffer, and every count is
|
|
33
|
+
* capped, so a malformed or hostile payload yields nullopt rather than misbehaving.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
#include "librats/util/rats_export.h"
|
|
37
|
+
#include "librats/core/address.h"
|
|
38
|
+
#include "librats/core/bytes.h"
|
|
39
|
+
|
|
40
|
+
#include <cstdint>
|
|
41
|
+
#include <optional>
|
|
42
|
+
#include <vector>
|
|
43
|
+
|
|
44
|
+
namespace librats {
|
|
45
|
+
|
|
46
|
+
struct RATS_API IdentifyMessage {
|
|
47
|
+
static constexpr uint8_t kVersion = 1; ///< IPs on the wire are raw bytes (4/16), not text
|
|
48
|
+
static constexpr size_t kMaxAddresses = 32; ///< cap advertised addresses
|
|
49
|
+
static constexpr size_t kMaxIpLength = 16; ///< a single IP is 4 (v4) or 16 (v6) bytes
|
|
50
|
+
|
|
51
|
+
/// Transport bitmask values, matching PeerTransports in peer/peer_info.h.
|
|
52
|
+
static constexpr uint8_t kTransportTcp = 1 << 0;
|
|
53
|
+
static constexpr uint8_t kTransportUdp = 1 << 1;
|
|
54
|
+
|
|
55
|
+
uint16_t listen_port = 0;
|
|
56
|
+
std::vector<Address> addresses; ///< sender's self-advertised dialable addrs
|
|
57
|
+
std::optional<Address> observed; ///< address sender saw the recipient at
|
|
58
|
+
/// Transports the sender accepts on listen_port. 0 means the sender did not
|
|
59
|
+
/// say (an older peer); treat that as "no better information", not as "none".
|
|
60
|
+
uint8_t transports = 0;
|
|
61
|
+
|
|
62
|
+
/// Serialise to the wire form. Addresses with an empty IP, an over-long IP, or
|
|
63
|
+
/// a zero port are skipped; at most kMaxAddresses are emitted.
|
|
64
|
+
Bytes encode() const;
|
|
65
|
+
|
|
66
|
+
/// Parse the wire form. Returns nullopt on an unknown version or any
|
|
67
|
+
/// truncation/inconsistency — the caller treats that as "no identify".
|
|
68
|
+
static std::optional<IdentifyMessage> decode(ByteView in);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
} // namespace librats
|