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,211 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file relay.h
|
|
5
|
+
* @brief Reaching a peer through a node both ends can already reach.
|
|
6
|
+
*
|
|
7
|
+
* The last rung of the connectivity ladder. A direct dial handles the easy cases;
|
|
8
|
+
* PortMappingService and HolePunch handle most of the rest. What is left is the
|
|
9
|
+
* pair of nodes for which no endpoint either one can advertise will ever work —
|
|
10
|
+
* a symmetric NAT on one side, a network that drops UDP and blocks inbound TCP,
|
|
11
|
+
* a punch that simply never lands. For them the only way through is to borrow a
|
|
12
|
+
* path: a node connected to both carries the bytes.
|
|
13
|
+
*
|
|
14
|
+
* ── What is relayed, and what is not ────────────────────────────────────────
|
|
15
|
+
* The relayed thing is a *byte stream*, not a message. It becomes a `Circuit` and
|
|
16
|
+
* a `RelayLink` (transport/relay_link.h), the reactor adopts it as an ordinary
|
|
17
|
+
* Connection, and everything above the Link runs unchanged — which is why:
|
|
18
|
+
*
|
|
19
|
+
* - the Noise_XX handshake is END TO END. The relay moves ciphertext it cannot
|
|
20
|
+
* read, cannot forge and cannot replay. It is a pipe, not a party, and it
|
|
21
|
+
* learns nothing beyond who is talking to whom and how much;
|
|
22
|
+
* - the peers authenticate each other's real keys, so a relay cannot substitute
|
|
23
|
+
* itself for either end;
|
|
24
|
+
* - every subsystem — pub/sub, file transfer, PEX — works over a relayed peer
|
|
25
|
+
* with no code of its own. To the application it is simply a peer, and
|
|
26
|
+
* PeerInfo::transport is the only thing that says otherwise.
|
|
27
|
+
*
|
|
28
|
+
* ── Finding a relay ─────────────────────────────────────────────────────────
|
|
29
|
+
* This stage covers the case where the two ends share a peer — the same topology
|
|
30
|
+
* HolePunch already relies on for its rendezvous, and the one a bootstrap or DHT
|
|
31
|
+
* mesh produces on its own. The initiator asks a few of its peers "do you hold
|
|
32
|
+
* this id?" (Probe) and opens a circuit with the first that says yes. Probing
|
|
33
|
+
* first, rather than opening with everybody at once, is what keeps a successful
|
|
34
|
+
* search from producing three redundant connections to the same peer — each with
|
|
35
|
+
* its own handshake — for the peer table to then tear two of down.
|
|
36
|
+
*
|
|
37
|
+
* Not covered here, deliberately: reaching a peer with whom we share NO peer. That
|
|
38
|
+
* needs reservations (a node asking a relay to hold a slot for it) and a way to
|
|
39
|
+
* advertise "reachable via R" as an address, which touches identify and PEX. The
|
|
40
|
+
* version byte and the free op-codes in the wire format leave room for it.
|
|
41
|
+
*
|
|
42
|
+
* ── Getting off the relay again ─────────────────────────────────────────────
|
|
43
|
+
* A circuit is a fallback, not a destination: it costs a third node bandwidth and
|
|
44
|
+
* a round trip. So when one comes up, this module asks HolePunch to try the target
|
|
45
|
+
* again — now that the two ends are peers, they can exchange what a punch needs
|
|
46
|
+
* over the very circuit that is carrying them. If the punch lands, PeerTable::add
|
|
47
|
+
* prefers the direct link at BOTH ends (any direct transport outranks Relay) and
|
|
48
|
+
* swaps the route with no disconnect event: the application never sees the seam.
|
|
49
|
+
*
|
|
50
|
+
* ── Carrying other peers' traffic ───────────────────────────────────────────
|
|
51
|
+
* Serving as a relay is off by default. Forwarding a rendezvous, as HolePunch
|
|
52
|
+
* does, is a few dozen bytes; forwarding a connection is somebody else's file
|
|
53
|
+
* transfer on your uplink, and that is a decision to be made rather than assumed.
|
|
54
|
+
* A node that turns it on is protected by, in order of how much they matter:
|
|
55
|
+
*
|
|
56
|
+
* - the end-to-end credit window (transport/relay_link.h), which bounds what one
|
|
57
|
+
* circuit can make the relay hold no matter what either end does. Without it a
|
|
58
|
+
* slow receiver would grow the relay's send queue until the relay dropped that
|
|
59
|
+
* peer entirely — losing all of its traffic, not just the circuit's;
|
|
60
|
+
* - forwarding only between peers it already holds. A relay never dials, never
|
|
61
|
+
* resolves an address, and can only ever deliver to somebody that chose to
|
|
62
|
+
* connect to it, so it cannot be turned into an open reflector;
|
|
63
|
+
* - no chaining. A circuit is refused if either end is itself only reachable
|
|
64
|
+
* through a relay, which is what stops loops and multi-hop amplification;
|
|
65
|
+
* - per-circuit byte and duration caps, per-peer and total circuit counts, and a
|
|
66
|
+
* rate limit on the requests themselves.
|
|
67
|
+
*
|
|
68
|
+
* ── Threading ───────────────────────────────────────────────────────────────
|
|
69
|
+
* Message handlers run on reactor threads; one worker thread drives attempt
|
|
70
|
+
* timeouts and the relay's own expiry. The tables are behind one mutex, which is
|
|
71
|
+
* never held across a call into a circuit or the reactor — a circuit is touched
|
|
72
|
+
* only by the reactor thread that owns its connection, which is by construction
|
|
73
|
+
* the thread that owns its carrier's (see node/circuit_service.h).
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
#include "librats/util/rats_export.h"
|
|
77
|
+
#include "librats/core/service_registry.h"
|
|
78
|
+
#include "librats/node/peer_network.h"
|
|
79
|
+
#include "librats/peer/peer_id.h"
|
|
80
|
+
#include "librats/subsystems/relay_service.h"
|
|
81
|
+
#include "librats/transport/relay_link.h" // Circuit::kDefaultWindow
|
|
82
|
+
|
|
83
|
+
#include <atomic>
|
|
84
|
+
#include <chrono>
|
|
85
|
+
#include <condition_variable>
|
|
86
|
+
#include <cstdint>
|
|
87
|
+
#include <memory>
|
|
88
|
+
#include <mutex>
|
|
89
|
+
#include <thread>
|
|
90
|
+
|
|
91
|
+
namespace librats {
|
|
92
|
+
|
|
93
|
+
class RATS_API Relay final : public Subsystem, public RelayService {
|
|
94
|
+
public:
|
|
95
|
+
struct Config {
|
|
96
|
+
// ── Using relays to reach peers we cannot dial ──────────────────────
|
|
97
|
+
|
|
98
|
+
/// Open circuits through other peers. Off makes this a relay-only node.
|
|
99
|
+
bool enable_client = true;
|
|
100
|
+
|
|
101
|
+
/// Peers asked whether they hold a target, per attempt. Small: each probe
|
|
102
|
+
/// is a question to somebody who probably cannot help, and one useful
|
|
103
|
+
/// answer is all an attempt needs.
|
|
104
|
+
size_t max_probes = 3;
|
|
105
|
+
|
|
106
|
+
/// Circuits this node may hold open as a client at once. Bounds both memory
|
|
107
|
+
/// and how many relayed peers a discovery module can talk us into.
|
|
108
|
+
size_t max_outbound_circuits = 8;
|
|
109
|
+
|
|
110
|
+
/// How long an attempt may take from the first probe to a live connection.
|
|
111
|
+
/// Covers the probe round trip, the circuit setup, and the handshake across
|
|
112
|
+
/// two hops.
|
|
113
|
+
std::chrono::milliseconds open_timeout{8000};
|
|
114
|
+
|
|
115
|
+
/// How long after a failed attempt before the same target may be tried
|
|
116
|
+
/// again. Only an attempt WE started ever ends in one.
|
|
117
|
+
std::chrono::milliseconds cooldown{60000};
|
|
118
|
+
|
|
119
|
+
// ── Accepting circuits opened to us ─────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/// Accept circuits other peers open toward us. Off makes this node
|
|
122
|
+
/// unreachable by relay, which is a choice a well-connected node can make.
|
|
123
|
+
bool accept_inbound = true;
|
|
124
|
+
|
|
125
|
+
/// Circuits opened TO us that may be live at once.
|
|
126
|
+
size_t max_inbound_circuits = 16;
|
|
127
|
+
|
|
128
|
+
// ── Carrying other peers' traffic ───────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/// Serve as a relay. OFF by default: unlike a hole-punch rendezvous, this
|
|
131
|
+
/// spends real bandwidth on somebody else's connection, so it is opted into
|
|
132
|
+
/// rather than assumed. A mesh in which nobody serves cannot relay at all.
|
|
133
|
+
bool serve = false;
|
|
134
|
+
|
|
135
|
+
/// Circuits this node will carry for others at once.
|
|
136
|
+
size_t max_circuits = 32;
|
|
137
|
+
|
|
138
|
+
/// Circuits one peer may have us carrying, so a single peer cannot take the
|
|
139
|
+
/// whole budget.
|
|
140
|
+
size_t max_circuits_per_peer = 2;
|
|
141
|
+
|
|
142
|
+
/// Bytes one circuit may move before it is closed. 0 removes the cap. The
|
|
143
|
+
/// default is generous for a fallback path and still bounds what one pair
|
|
144
|
+
/// of peers can spend of ours.
|
|
145
|
+
uint64_t max_bytes_per_circuit = 64ull * 1024 * 1024;
|
|
146
|
+
|
|
147
|
+
/// How long one circuit may live. 0 removes the cap.
|
|
148
|
+
std::chrono::seconds max_circuit_duration{600};
|
|
149
|
+
|
|
150
|
+
/// Requests (probes and circuit openings) one peer may make per window.
|
|
151
|
+
size_t request_budget = 8;
|
|
152
|
+
std::chrono::milliseconds request_window{1000};
|
|
153
|
+
|
|
154
|
+
// ── Both ends ───────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
/// Bytes this node is willing to have in flight per circuit — the credit
|
|
157
|
+
/// window it advertises (see transport/relay_link.h). Raising it makes a
|
|
158
|
+
/// relayed peer faster on a long path and asks the relay to hold more.
|
|
159
|
+
uint32_t window = Circuit::kDefaultWindow;
|
|
160
|
+
|
|
161
|
+
/// When a relayed peer connects, ask HolePunch to try it directly, so the
|
|
162
|
+
/// circuit is replaced by a direct link as soon as one is possible. Costs
|
|
163
|
+
/// nothing when no HolePunch is attached.
|
|
164
|
+
bool upgrade_with_hole_punch = true;
|
|
165
|
+
|
|
166
|
+
/// Bookkeeping cadence — attempt timeouts, cooldowns, circuit expiry.
|
|
167
|
+
std::chrono::milliseconds tick{250};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
Relay();
|
|
171
|
+
explicit Relay(Config config);
|
|
172
|
+
~Relay() override;
|
|
173
|
+
|
|
174
|
+
void attach(NodeContext& ctx) override;
|
|
175
|
+
void start() override;
|
|
176
|
+
void stop() override;
|
|
177
|
+
|
|
178
|
+
/// @copydoc RelayService::connect_via_relay
|
|
179
|
+
bool connect_via_relay(const PeerId& target) override;
|
|
180
|
+
|
|
181
|
+
// — diagnostics and tests —
|
|
182
|
+
/// Circuits this node terminates (relayed peers, live or still being set up).
|
|
183
|
+
size_t circuits() const;
|
|
184
|
+
/// Circuits this node is carrying on behalf of other peers.
|
|
185
|
+
size_t carried_circuits() const;
|
|
186
|
+
/// Bytes forwarded on behalf of other peers.
|
|
187
|
+
uint64_t carried_bytes() const;
|
|
188
|
+
/// Attempts to reach a peer through a relay that are currently running.
|
|
189
|
+
size_t attempts() const;
|
|
190
|
+
|
|
191
|
+
private:
|
|
192
|
+
/// Everything mutable, held by shared_ptr because a Connection — and therefore
|
|
193
|
+
/// the Link that reports its circuit released — can outlive this subsystem: the
|
|
194
|
+
/// node stops subsystems before it stops the reactors that own the connections.
|
|
195
|
+
struct State;
|
|
196
|
+
|
|
197
|
+
Config config_;
|
|
198
|
+
std::shared_ptr<State> state_;
|
|
199
|
+
/// Kept from attach() so start() can resolve HolePunchService — which may be
|
|
200
|
+
/// attached after us, and is only guaranteed to have registered by then.
|
|
201
|
+
ServiceRegistry* services_ = nullptr;
|
|
202
|
+
|
|
203
|
+
std::thread worker_;
|
|
204
|
+
std::atomic<bool> running_{false};
|
|
205
|
+
std::mutex worker_mutex_;
|
|
206
|
+
std::condition_variable worker_cv_;
|
|
207
|
+
|
|
208
|
+
void loop();
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
} // namespace librats
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file relay_service.h
|
|
5
|
+
* @brief Capability that lets a sibling module ask for a peer to be reached
|
|
6
|
+
* through a third node, by PeerId.
|
|
7
|
+
*
|
|
8
|
+
* Published by Relay via ServiceRegistry (see service_registry.h). It is the last
|
|
9
|
+
* rung of the connectivity ladder — a direct dial, then a hole punch, then this —
|
|
10
|
+
* and the module that gives up on the rung above should not have to know anything
|
|
11
|
+
* about how the rung below works. HolePunch, for instance, learns that a target is
|
|
12
|
+
* unreachable (a symmetric NAT it declined to punch, or a rendezvous that ran out
|
|
13
|
+
* of attempts) and this is the whole contract it needs to hand the id on: no relay
|
|
14
|
+
* selection, no circuits, no policy, all of which are Relay's own business.
|
|
15
|
+
*
|
|
16
|
+
* Resolving it returns nullptr when relaying is not enabled, which is exactly what
|
|
17
|
+
* makes the fallback optional at the consumer's side:
|
|
18
|
+
*
|
|
19
|
+
* if (auto* relay = ctx.services.get<RelayService>()) relay->connect_via_relay(id);
|
|
20
|
+
*
|
|
21
|
+
* The provider registers during attach(), so resolve it in start() (every attach()
|
|
22
|
+
* runs before any start()) rather than in your own attach(), where the provider may
|
|
23
|
+
* not have been attached yet. The pointer is NON-owning and valid while the node is.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
#include "librats/util/rats_export.h"
|
|
27
|
+
#include "librats/peer/peer_id.h"
|
|
28
|
+
|
|
29
|
+
namespace librats {
|
|
30
|
+
|
|
31
|
+
struct RATS_API RelayService {
|
|
32
|
+
virtual ~RelayService() = default;
|
|
33
|
+
|
|
34
|
+
/// Try to reach `target` through a node both ends are connected to.
|
|
35
|
+
/// Non-blocking; success surfaces as an ordinary peer-connected event, and the
|
|
36
|
+
/// resulting peer is ordinary in every way except that its bytes take a detour
|
|
37
|
+
/// (PeerInfo::transport is TransportKind::Relay).
|
|
38
|
+
///
|
|
39
|
+
/// @return whether an attempt actually started — false is routine (already
|
|
40
|
+
/// connected, an attempt is already running, the target is in cooldown,
|
|
41
|
+
/// or this node has no peer that could carry the circuit) and needs no
|
|
42
|
+
/// handling by the caller.
|
|
43
|
+
virtual bool connect_via_relay(const PeerId& target) = 0;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} // namespace librats
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#include "librats/transport/connection.h"
|
|
2
|
+
#include "librats/transport/reactor.h"
|
|
3
|
+
#include "librats/core/io_poller.h"
|
|
4
|
+
#include "librats/util/logger.h"
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <cerrno>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
|
|
10
|
+
namespace librats {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
constexpr size_t kRecvChunk = 16 * 1024; ///< bytes offered to each recv()
|
|
15
|
+
|
|
16
|
+
/// Ceiling on how far rx_ may be grown on the strength of a *declared* block length
|
|
17
|
+
/// alone. Sizing the buffer for the whole block up front (as libtorrent does, growing
|
|
18
|
+
/// straight to packet_size) turns a large frame into one allocation instead of a
|
|
19
|
+
/// 1.5x-at-a-time climb that memmoves everything received so far at every step. But
|
|
20
|
+
/// the length is the peer's word: a 4-byte prefix claiming kMaxBlockSize must not
|
|
21
|
+
/// make us allocate 64 MiB for a peer that then sends nothing. Past this, growth
|
|
22
|
+
/// falls back to geometric, so the allocation only ever tracks bytes that really
|
|
23
|
+
/// arrived — a big block still costs a handful of reallocations, not tens.
|
|
24
|
+
constexpr size_t kMaxEagerReserve = 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/// Frames up to this size are copied into the send queue rather than moved into a
|
|
27
|
+
/// chunk of their own (see queue_block). Matches the queue's scratch chunk, so a
|
|
28
|
+
/// small frame and its length prefix land in the same allocation.
|
|
29
|
+
constexpr size_t kInlineBlockLimit = ChainedSendBuffer::kScratchCapacity;
|
|
30
|
+
|
|
31
|
+
} // namespace
|
|
32
|
+
|
|
33
|
+
Connection::Connection(ConnId id, std::unique_ptr<Link> link, ConnRole role,
|
|
34
|
+
Reactor& reactor, ConnectionDelegate& delegate)
|
|
35
|
+
: id_(id), link_(std::move(link)), role_(role), reactor_(reactor), delegate_(delegate) {}
|
|
36
|
+
|
|
37
|
+
Connection::~Connection() = default;
|
|
38
|
+
|
|
39
|
+
IpAddress Connection::remote_ip() const {
|
|
40
|
+
// The peer's source IP as the transport sees it, with no textual round-trip.
|
|
41
|
+
// (The source port is not dialable — ephemeral on TCP, NAT-rewritten on UDP —
|
|
42
|
+
// so it is discarded; identify supplies the real listen port.)
|
|
43
|
+
const auto ep = link_->remote_endpoint();
|
|
44
|
+
return ep ? ep->ip : IpAddress{};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
uint8_t Connection::reactor_index() const noexcept { return reactor_.index(); }
|
|
48
|
+
|
|
49
|
+
// ── Outbound application frames ─────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
bool Connection::send(FrameHeader header, ByteView payload) {
|
|
52
|
+
if (state_ != ConnState::Established) return false; // frames only flow post-handshake
|
|
53
|
+
|
|
54
|
+
Bytes inner;
|
|
55
|
+
framer::encode_message(inner, header, payload);
|
|
56
|
+
|
|
57
|
+
Bytes cipher;
|
|
58
|
+
if (!session_->encrypt(inner, cipher)) {
|
|
59
|
+
LOG_ERROR("connection", "Peer " << remote_id_.short_hex() << " encrypt failed");
|
|
60
|
+
reactor_.close(id_, CloseReason::ProtocolError);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
queue_block(std::move(cipher));
|
|
65
|
+
|
|
66
|
+
const size_t backlog_now = backlog();
|
|
67
|
+
if (backlog_now > send_high_water_) {
|
|
68
|
+
LOG_WARN("connection", "Peer " << remote_id_.short_hex() << " over send high-water ("
|
|
69
|
+
<< backlog_now << " B); closing as slow consumer");
|
|
70
|
+
close_reason_ = CloseReason::SlowConsumer;
|
|
71
|
+
state_ = ConnState::Closing;
|
|
72
|
+
reactor_.close(id_, CloseReason::SlowConsumer);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The queue has grown past what a caller should keep adding to. Everything
|
|
77
|
+
// still goes out — nothing is dropped here — but the answer to "may I send
|
|
78
|
+
// more?" becomes no, and stays no until the queue drains back under the mark
|
|
79
|
+
// and on_writable says so. Without this an application has no way at all to
|
|
80
|
+
// tell that it is outrunning the link, and the only thing that eventually
|
|
81
|
+
// tells it is the disconnection above.
|
|
82
|
+
if (!over_low_water_ && backlog_now > send_low_water_) {
|
|
83
|
+
over_low_water_ = true;
|
|
84
|
+
delegate_.on_writable_changed(*this, false);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Aggregate rather than write through. Several frames produced in one batch
|
|
88
|
+
// of reactor tasks belong in ONE write, and flushing on each of them costs a
|
|
89
|
+
// system call per frame — measured at roughly ten times the throughput on
|
|
90
|
+
// small messages, on both transports. The flush happens at the end of the
|
|
91
|
+
// reactor turn (Reactor::flush_dirty), which is still the same turn: nothing
|
|
92
|
+
// waits on a timer and no latency is added.
|
|
93
|
+
//
|
|
94
|
+
// Past a point, though, waiting buys nothing: a backlog this size already
|
|
95
|
+
// fills a write, and letting it grow further would only hold memory and
|
|
96
|
+
// delay the bytes. So a large queue goes out at once, exactly as before.
|
|
97
|
+
if (tx_.size() >= kCoalesceLimit) {
|
|
98
|
+
if (!flush()) reactor_.close(id_, close_reason_);
|
|
99
|
+
} else if (!flush_queued_) {
|
|
100
|
+
flush_queued_ = true;
|
|
101
|
+
reactor_.mark_dirty(id_);
|
|
102
|
+
}
|
|
103
|
+
return !over_low_water_;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bool Connection::flush_pending() {
|
|
107
|
+
flush_queued_ = false;
|
|
108
|
+
return flush();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void Connection::queue_block(Bytes body) {
|
|
112
|
+
// The length prefix is queued as its own slice instead of being spliced in front
|
|
113
|
+
// of `body` — gather I/O reunites them in one writev, so framing costs no copy.
|
|
114
|
+
uint8_t prefix[framer::kLengthPrefixSize];
|
|
115
|
+
framer::encode_block_header(prefix, body.size());
|
|
116
|
+
tx_.append(ByteView(prefix, sizeof(prefix)));
|
|
117
|
+
|
|
118
|
+
// Small frame: copy it in, so prefix and body share one packed chunk. A memcpy of
|
|
119
|
+
// a few hundred bytes is cheaper than the allocation it saves — and it keeps a
|
|
120
|
+
// backlog of small frames from costing a chunk each.
|
|
121
|
+
// Large frame: move it, so a megabyte-sized payload is never copied to be framed.
|
|
122
|
+
if (body.size() <= kInlineBlockLimit) tx_.append(ByteView(body));
|
|
123
|
+
else tx_.append(std::move(body));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Handshake lifecycle ─────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
void Connection::start_handshake() {
|
|
129
|
+
if (state_ == ConnState::Connecting) begin_handshake();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
void Connection::begin_handshake() {
|
|
133
|
+
state_ = ConnState::Handshaking;
|
|
134
|
+
// The transport is up, so the write-interest raised for the connect itself is
|
|
135
|
+
// no longer wanted; flush() re-arms it if the handshake outgrows the link.
|
|
136
|
+
want_write_ = false;
|
|
137
|
+
link_->want_write(false);
|
|
138
|
+
|
|
139
|
+
handshaker_ = reactor_.security().create(role_);
|
|
140
|
+
Bytes out;
|
|
141
|
+
if (!handshaker_->start(out)) {
|
|
142
|
+
reactor_.close(id_, CloseReason::HandshakeFailed);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!out.empty()) {
|
|
146
|
+
queue_block(std::move(out)); // initiator's first message
|
|
147
|
+
if (!flush()) { reactor_.close(id_, close_reason_); return; }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
bool Connection::drive_handshake(ByteView body) {
|
|
152
|
+
Bytes out;
|
|
153
|
+
auto outcome = handshaker_->consume(body, out);
|
|
154
|
+
if (outcome.status == Handshaker::Outcome::Failed) {
|
|
155
|
+
return fail(CloseReason::HandshakeFailed);
|
|
156
|
+
}
|
|
157
|
+
if (!out.empty()) {
|
|
158
|
+
queue_block(std::move(out)); // reply (responder's message, or initiator's final)
|
|
159
|
+
if (!flush()) return false;
|
|
160
|
+
}
|
|
161
|
+
if (outcome.status == Handshaker::Outcome::Done) {
|
|
162
|
+
session_ = std::move(outcome.session);
|
|
163
|
+
remote_id_ = outcome.remote_id;
|
|
164
|
+
handshaker_.reset();
|
|
165
|
+
complete_established();
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void Connection::cancel_establish_timer() {
|
|
171
|
+
if (establish_timer_ != kInvalidTimerId) {
|
|
172
|
+
reactor_.cancel(establish_timer_);
|
|
173
|
+
establish_timer_ = kInvalidTimerId;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
void Connection::complete_established() {
|
|
178
|
+
state_ = ConnState::Established;
|
|
179
|
+
cancel_establish_timer();
|
|
180
|
+
LOG_DEBUG("connection", "Peer " << remote_id_.short_hex() << " established ("
|
|
181
|
+
<< (role_ == ConnRole::Inbound ? "inbound" : "outbound")
|
|
182
|
+
<< (is_secure() ? ", encrypted)" : ", plaintext)"));
|
|
183
|
+
delegate_.on_established(*this);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Inbound ─────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
bool Connection::on_readable() {
|
|
189
|
+
// Drain the kernel buffer (the socket is non-blocking), parsing as we go. The
|
|
190
|
+
// parse step is inside the loop on purpose: it keeps rx_ down to one in-flight
|
|
191
|
+
// block no matter how fast the peer sends, where draining first and parsing
|
|
192
|
+
// afterwards would let rx_ grow to everything the peer managed to deliver.
|
|
193
|
+
//
|
|
194
|
+
// We must drain to would-block (or to a short read, which means the kernel
|
|
195
|
+
// buffer is now empty) — the kqueue backend is edge-triggered, so stopping with
|
|
196
|
+
// data still queued would leave the connection stalled until the *next* byte.
|
|
197
|
+
while (true) {
|
|
198
|
+
const ByteSpan into = rx_.prepare(read_size());
|
|
199
|
+
|
|
200
|
+
const Link::IoResult r = link_->read(into);
|
|
201
|
+
if (r.status == Link::Status::Closed) {
|
|
202
|
+
if (!process_blocks()) return false; // deliver what the peer sent before FIN
|
|
203
|
+
return fail(CloseReason::PeerClosed);
|
|
204
|
+
}
|
|
205
|
+
if (r.status == Link::Status::Error) return fail(link_->error_reason());
|
|
206
|
+
if (r.status == Link::Status::WouldBlock) break;
|
|
207
|
+
|
|
208
|
+
rx_.commit(r.bytes);
|
|
209
|
+
if (!process_blocks()) return false;
|
|
210
|
+
|
|
211
|
+
if (r.bytes < into.size()) break; // link drained
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
size_t Connection::read_size() const {
|
|
217
|
+
// Not established yet: the peer is still anonymous, so its declared length buys it
|
|
218
|
+
// nothing. A handshake block is a Noise message plus the protocol id — hundreds of
|
|
219
|
+
// bytes — so there is no large allocation to save here, while honouring the length
|
|
220
|
+
// would let four bytes from an unauthenticated socket reserve kMaxEagerReserve. rx_
|
|
221
|
+
// still grows geometrically to hold whatever actually arrives, so an unusually large
|
|
222
|
+
// handshake block is served just as correctly, only without the eager reserve.
|
|
223
|
+
if (state_ != ConnState::Established) return kRecvChunk;
|
|
224
|
+
|
|
225
|
+
// Ask for the rest of the block we are mid-way through, so a large frame lands in
|
|
226
|
+
// one allocation rather than a series of 1.5x growth steps. Bounded by
|
|
227
|
+
// kMaxEagerReserve, since rx_need_ is a length the *peer* declared.
|
|
228
|
+
//
|
|
229
|
+
// Note this asks for the remainder even when it is *smaller* than kRecvChunk. It
|
|
230
|
+
// is tempting to floor it at kRecvChunk ("read as much as we can anyway"), but
|
|
231
|
+
// prepare(n) is a demand for n bytes of free tail, and a buffer sized exactly for
|
|
232
|
+
// the block has no kRecvChunk of tail left near the end of it — so the floor would
|
|
233
|
+
// force one last 1.5x grow (memcpy'ing the whole block that already arrived) for
|
|
234
|
+
// room that the block does not need. Nothing is lost by asking for less: prepare()
|
|
235
|
+
// hands back the *entire* free tail regardless, so the recv() is just as big.
|
|
236
|
+
if (rx_need_ > rx_.size()) return (std::min)(rx_need_ - rx_.size(), kMaxEagerReserve);
|
|
237
|
+
return kRecvChunk;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
bool Connection::process_blocks() {
|
|
241
|
+
rx_need_ = 0; // recomputed below: 0 == no block is mid-flight
|
|
242
|
+
|
|
243
|
+
// Body views point into rx_, so each block must be consumed before the next is
|
|
244
|
+
// decoded (and before any recv() reallocates the buffer under us).
|
|
245
|
+
while (!rx_.empty()) {
|
|
246
|
+
const auto block = framer::try_take_block(rx_.data(), rx_.size());
|
|
247
|
+
if (block.status == framer::Block::Incomplete) {
|
|
248
|
+
rx_need_ = block.needed; // 0 while even the length prefix is short
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
if (block.status == framer::Block::Error) return fail(CloseReason::ProtocolError);
|
|
252
|
+
|
|
253
|
+
const bool keep = (state_ == ConnState::Handshaking) ? drive_handshake(block.body)
|
|
254
|
+
: deliver_frame(block.body);
|
|
255
|
+
rx_.consume(block.consumed);
|
|
256
|
+
if (!keep) return false;
|
|
257
|
+
if (state_ == ConnState::Closing || state_ == ConnState::Closed) return false;
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
bool Connection::deliver_frame(ByteView body) {
|
|
263
|
+
if (!session_) return fail(CloseReason::ProtocolError); // data before handshake
|
|
264
|
+
|
|
265
|
+
Bytes plain;
|
|
266
|
+
if (!session_->decrypt(body, plain)) return fail(CloseReason::ProtocolError);
|
|
267
|
+
|
|
268
|
+
auto msg = framer::parse_message(plain);
|
|
269
|
+
if (!msg.ok) return fail(CloseReason::ProtocolError);
|
|
270
|
+
|
|
271
|
+
delegate_.on_frame(*this, msg.frame);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
bool Connection::on_writable() {
|
|
276
|
+
// Finish the transport-level connect (a non-blocking TCP connect, or a Syn
|
|
277
|
+
// that has just been acknowledged), then begin the handshake.
|
|
278
|
+
if (state_ == ConnState::Connecting) {
|
|
279
|
+
if (!link_->connect_completed()) {
|
|
280
|
+
LOG_DEBUG("connection", "Peer " << id_ << " connect failed");
|
|
281
|
+
return fail(CloseReason::ConnectFailed);
|
|
282
|
+
}
|
|
283
|
+
begin_handshake();
|
|
284
|
+
if (state_ == ConnState::Closing) return false;
|
|
285
|
+
}
|
|
286
|
+
return flush();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
bool Connection::on_error() {
|
|
290
|
+
return fail(link_->error_reason());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Send buffer flush + write interest ──────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
bool Connection::flush() {
|
|
296
|
+
while (!tx_.empty()) {
|
|
297
|
+
// Hand the whole backlog to the kernel at once: a burst of queued frames
|
|
298
|
+
// (and the length prefix that precedes each one) leaves in a single syscall
|
|
299
|
+
// instead of one per chunk.
|
|
300
|
+
ByteView slices[kMaxSendSlices];
|
|
301
|
+
const size_t count = tx_.gather(slices, kMaxSendSlices);
|
|
302
|
+
|
|
303
|
+
const Link::IoResult r = link_->write(slices, count);
|
|
304
|
+
if (r.status == Link::Status::Ok) { tx_.pop_front(r.bytes); continue; }
|
|
305
|
+
if (r.status == Link::Status::WouldBlock) break; // retry on the next writable event
|
|
306
|
+
return fail(link_->error_reason());
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (tx_.empty()) disarm_write();
|
|
310
|
+
else arm_write();
|
|
311
|
+
|
|
312
|
+
// Drained back under the mark: whoever was told to stop can start again. The
|
|
313
|
+
// re-entrancy guard is what keeps a handler that answers by sending from
|
|
314
|
+
// recursing through flush() — it may queue, and the next drain reports the
|
|
315
|
+
// next opening, but it cannot nest.
|
|
316
|
+
if (over_low_water_ && !in_writable_ && backlog() <= send_low_water_) {
|
|
317
|
+
over_low_water_ = false;
|
|
318
|
+
in_writable_ = true;
|
|
319
|
+
delegate_.on_writable_changed(*this, true);
|
|
320
|
+
in_writable_ = false;
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void Connection::arm_write() {
|
|
326
|
+
if (want_write_) return;
|
|
327
|
+
want_write_ = true;
|
|
328
|
+
link_->want_write(true);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
void Connection::disarm_write() {
|
|
332
|
+
if (!want_write_) return;
|
|
333
|
+
want_write_ = false;
|
|
334
|
+
link_->want_write(false);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
bool Connection::fail(CloseReason reason) {
|
|
338
|
+
close_reason_ = reason;
|
|
339
|
+
state_ = ConnState::Closing;
|
|
340
|
+
return false; // tells the reactor to tear this connection down
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
} // namespace librats
|