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
|
@@ -1,2378 +0,0 @@
|
|
|
1
|
-
#include "librats.h"
|
|
2
|
-
#include "os.h"
|
|
3
|
-
#include "network_utils.h"
|
|
4
|
-
#include "network_monitor.h" // complete type for unique_ptr<NetworkMonitor> member
|
|
5
|
-
#include "version.h"
|
|
6
|
-
#include <algorithm>
|
|
7
|
-
#include <array>
|
|
8
|
-
#include <random>
|
|
9
|
-
#include <sstream>
|
|
10
|
-
#include <iomanip>
|
|
11
|
-
#include <string_view>
|
|
12
|
-
|
|
13
|
-
#include "librats_log_macros.h"
|
|
14
|
-
|
|
15
|
-
namespace librats {
|
|
16
|
-
|
|
17
|
-
// Configuration file constants
|
|
18
|
-
const std::string RatsClient::CONFIG_FILE_NAME = "config.json";
|
|
19
|
-
const std::string RatsClient::PEERS_FILE_NAME = "peers.rats";
|
|
20
|
-
const std::string RatsClient::PEERS_EVER_FILE_NAME = "peers_ever.rats";
|
|
21
|
-
|
|
22
|
-
// =========================================================================
|
|
23
|
-
// Constructor and Destructor
|
|
24
|
-
// =========================================================================
|
|
25
|
-
|
|
26
|
-
RatsClient::RatsClient(int listen_port, int max_peers, const std::string& bind_address)
|
|
27
|
-
: listen_port_(listen_port),
|
|
28
|
-
bind_address_(bind_address),
|
|
29
|
-
max_peers_(max_peers),
|
|
30
|
-
server_socket_(INVALID_SOCKET_VALUE),
|
|
31
|
-
running_(false),
|
|
32
|
-
// [1] Configuration persistence
|
|
33
|
-
data_directory_("."),
|
|
34
|
-
// [2] Custom protocol configuration
|
|
35
|
-
custom_protocol_name_("rats"),
|
|
36
|
-
custom_protocol_version_("1.0"),
|
|
37
|
-
// [3] Encryption state
|
|
38
|
-
encryption_enabled_(false),
|
|
39
|
-
noise_keypair_initialized_(false),
|
|
40
|
-
// Automatic discovery
|
|
41
|
-
auto_discovery_running_(false) {
|
|
42
|
-
// Load configuration (this will generate peer ID if needed)
|
|
43
|
-
load_configuration();
|
|
44
|
-
|
|
45
|
-
// Initialize modules
|
|
46
|
-
initialize_modules();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
RatsClient::~RatsClient() {
|
|
50
|
-
stop();
|
|
51
|
-
// Destroy modules
|
|
52
|
-
destroy_modules();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// =========================================================================
|
|
56
|
-
// Modules Initialization and Destruction
|
|
57
|
-
// =========================================================================
|
|
58
|
-
|
|
59
|
-
void RatsClient::initialize_modules() {
|
|
60
|
-
// Initialize GossipSub
|
|
61
|
-
if (!gossipsub_) {
|
|
62
|
-
LOG_CLIENT_INFO("Initializing GossipSub");
|
|
63
|
-
gossipsub_ = std::make_unique<GossipSub>(*this);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Initialize File Transfer Manager
|
|
67
|
-
if (!file_transfer_manager_) {
|
|
68
|
-
LOG_CLIENT_INFO("Initializing File Transfer Manager");
|
|
69
|
-
file_transfer_manager_ = std::make_unique<FileTransferManager>(*this);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
void RatsClient::destroy_modules() {
|
|
74
|
-
if (gossipsub_) {
|
|
75
|
-
LOG_CLIENT_INFO("Destroying GossipSub");
|
|
76
|
-
gossipsub_.reset();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (file_transfer_manager_) {
|
|
80
|
-
LOG_CLIENT_INFO("Destroying File Transfer Manager");
|
|
81
|
-
file_transfer_manager_.reset();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// =========================================================================
|
|
86
|
-
// Core Lifecycle Management
|
|
87
|
-
// =========================================================================
|
|
88
|
-
|
|
89
|
-
bool RatsClient::start() {
|
|
90
|
-
if (running_.load()) {
|
|
91
|
-
LOG_CLIENT_WARN("RatsClient is already running");
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
LOG_CLIENT_INFO("Starting RatsClient on port " << listen_port_ <<
|
|
96
|
-
(bind_address_.empty() ? "" : " bound to " + bind_address_));
|
|
97
|
-
|
|
98
|
-
// Print system information for debugging and log analysis
|
|
99
|
-
SystemInfo sys_info = get_system_info();
|
|
100
|
-
LOG_CLIENT_INFO("=== System Information ===");
|
|
101
|
-
LOG_CLIENT_INFO("OS: " << sys_info.os_name << " " << sys_info.os_version);
|
|
102
|
-
LOG_CLIENT_INFO("Architecture: " << sys_info.architecture);
|
|
103
|
-
LOG_CLIENT_INFO("Hostname: " << sys_info.hostname);
|
|
104
|
-
LOG_CLIENT_INFO("CPU: " << sys_info.cpu_model);
|
|
105
|
-
LOG_CLIENT_INFO("CPU Cores: " << sys_info.cpu_cores << " physical, " << sys_info.cpu_logical_cores << " logical");
|
|
106
|
-
LOG_CLIENT_INFO("Memory: " << sys_info.total_memory_mb << " MB total, " << sys_info.available_memory_mb << " MB available");
|
|
107
|
-
LOG_CLIENT_INFO("===========================");
|
|
108
|
-
|
|
109
|
-
// Initialize socket library first (required for all socket operations)
|
|
110
|
-
init_socket_library();
|
|
111
|
-
|
|
112
|
-
// Initialize encryption
|
|
113
|
-
if (!initialize_encryption(encryption_enabled_)) {
|
|
114
|
-
LOG_CLIENT_ERROR("Failed to initialize encryption");
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Initialize local interface addresses for connection blocking
|
|
119
|
-
initialize_local_addresses();
|
|
120
|
-
|
|
121
|
-
// Create dual-stack server socket (supports both IPv4 and IPv6)
|
|
122
|
-
server_socket_ = create_tcp_server(listen_port_, 5, bind_address_);
|
|
123
|
-
// Fallback to free port
|
|
124
|
-
if (!is_valid_socket(server_socket_) && listen_port_ > 0) {
|
|
125
|
-
// Requested port is not available, try ephemeral port as fallback
|
|
126
|
-
int original_port = listen_port_;
|
|
127
|
-
LOG_CLIENT_WARN("TCP port " << original_port << " is not available, falling back to ephemeral port");
|
|
128
|
-
server_socket_ = create_tcp_server(0, 5, bind_address_);
|
|
129
|
-
if (is_valid_socket(server_socket_)) {
|
|
130
|
-
listen_port_ = get_bound_port(server_socket_);
|
|
131
|
-
LOG_CLIENT_INFO("Fell back from port " << original_port << " to ephemeral port " << listen_port_);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (!is_valid_socket(server_socket_)) {
|
|
135
|
-
LOG_CLIENT_ERROR("Failed to create server socket on port " << listen_port_ <<
|
|
136
|
-
(bind_address_.empty() ? "" : " bound to " + bind_address_));
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Update listen_port_ with actual bound port if ephemeral port was requested
|
|
141
|
-
if (listen_port_ == 0) {
|
|
142
|
-
listen_port_ = get_bound_port(server_socket_);
|
|
143
|
-
if (listen_port_ == 0) {
|
|
144
|
-
LOG_CLIENT_WARN("Failed to get actual bound port - using port 0");
|
|
145
|
-
} else {
|
|
146
|
-
LOG_CLIENT_INFO("Server bound to ephemeral port " << listen_port_);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Set server socket to non-blocking for the IO poller
|
|
151
|
-
set_socket_nonblocking(server_socket_);
|
|
152
|
-
|
|
153
|
-
// Create platform-optimal IO poller and register server socket
|
|
154
|
-
poller_ = IOPoller::create();
|
|
155
|
-
poller_->add(server_socket_, PollIn);
|
|
156
|
-
LOG_CLIENT_INFO("IO poller backend: " << poller_->name());
|
|
157
|
-
|
|
158
|
-
running_.store(true);
|
|
159
|
-
|
|
160
|
-
// Start IO thread (single-threaded event loop for all sockets)
|
|
161
|
-
io_thread_ = std::thread(&RatsClient::io_loop, this);
|
|
162
|
-
|
|
163
|
-
// Start management thread (handshake timeouts, reconnection, thread cleanup)
|
|
164
|
-
management_thread_ = std::thread(&RatsClient::management_loop, this);
|
|
165
|
-
|
|
166
|
-
// Start GossipSub
|
|
167
|
-
if (gossipsub_ && !gossipsub_->start()) {
|
|
168
|
-
LOG_CLIENT_WARN("Failed to start GossipSub - continuing without it");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Start automatic port forwarding (UPnP/NAT-PMP) for the bound listen port.
|
|
172
|
-
// No-op when disabled; runs discovery/mapping on its own background threads.
|
|
173
|
-
start_port_mapping();
|
|
174
|
-
|
|
175
|
-
// React to host network changes (IP/interface/route): renew port mappings,
|
|
176
|
-
// re-discover the public address and re-announce. No-op when disabled.
|
|
177
|
-
start_network_monitor();
|
|
178
|
-
|
|
179
|
-
LOG_CLIENT_INFO("RatsClient started successfully on port " << listen_port_);
|
|
180
|
-
|
|
181
|
-
// Attempt to reconnect to saved peers
|
|
182
|
-
add_managed_thread(std::thread([this]() {
|
|
183
|
-
// Give the server some time to fully initialize
|
|
184
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(PEER_RECONNECT_DELAY_MS));
|
|
185
|
-
int reconnect_attempts = load_and_reconnect_peers();
|
|
186
|
-
if (reconnect_attempts > 0) {
|
|
187
|
-
LOG_CLIENT_INFO("Attempted to reconnect to " << reconnect_attempts << " saved peers");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Also attempt to reconnect to historical peers if not at peer limit
|
|
191
|
-
if (!is_peer_limit_reached()) {
|
|
192
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(HISTORICAL_RECONNECT_DELAY_MS));
|
|
193
|
-
int historical_attempts = load_and_reconnect_historical_peers();
|
|
194
|
-
if (historical_attempts > 0) {
|
|
195
|
-
LOG_CLIENT_INFO("Attempted to reconnect to " << historical_attempts << " historical peers");
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}), "peer-reconnection");
|
|
199
|
-
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
void RatsClient::stop() {
|
|
204
|
-
if (!running_.load()) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
LOG_CLIENT_INFO("Stopping RatsClient");
|
|
209
|
-
|
|
210
|
-
// Stop network-change detection FIRST and join its recovery worker: that
|
|
211
|
-
// worker touches the port-mapping backends and the DHT clients, both torn
|
|
212
|
-
// down below, so it must not be running past this point.
|
|
213
|
-
stop_network_monitor();
|
|
214
|
-
|
|
215
|
-
// Remove port mappings and stop UPnP/NAT-PMP backends before tearing down
|
|
216
|
-
// sockets (best-effort cleanup so we don't leave stale router mappings).
|
|
217
|
-
stop_port_mapping();
|
|
218
|
-
|
|
219
|
-
// Stop GossipSub (can broadcast stop message)
|
|
220
|
-
if (gossipsub_) {
|
|
221
|
-
gossipsub_->stop();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Trigger immediate shutdown of all background threads
|
|
225
|
-
shutdown_all_threads();
|
|
226
|
-
|
|
227
|
-
// Stop DHT discovery (this will also stop automatic discovery)
|
|
228
|
-
stop_dht_discovery();
|
|
229
|
-
|
|
230
|
-
// Stop mDNS discovery
|
|
231
|
-
stop_mdns_discovery();
|
|
232
|
-
|
|
233
|
-
// Close server socket to break accept loop
|
|
234
|
-
if (is_valid_socket(server_socket_)) {
|
|
235
|
-
close_socket(server_socket_, true);
|
|
236
|
-
server_socket_ = INVALID_SOCKET_VALUE;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Clear reconnection queue to prevent reconnection attempts during shutdown
|
|
240
|
-
{
|
|
241
|
-
std::lock_guard<std::mutex> lock(reconnect_mutex_);
|
|
242
|
-
reconnect_queue_.clear();
|
|
243
|
-
manual_disconnect_peers_.clear();
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Close all peer connections and remove from poller
|
|
247
|
-
{
|
|
248
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
249
|
-
LOG_CLIENT_INFO("Closing " << peers_.size() << " peer connections");
|
|
250
|
-
for (const auto& pair : peers_) {
|
|
251
|
-
const RatsPeer& peer = pair.second;
|
|
252
|
-
if (poller_) poller_->remove(peer.socket);
|
|
253
|
-
close_socket(peer.socket, true);
|
|
254
|
-
}
|
|
255
|
-
peers_.clear();
|
|
256
|
-
socket_to_peer_id_.clear();
|
|
257
|
-
address_to_peer_id_.clear();
|
|
258
|
-
validated_peer_count_.store(0, std::memory_order_relaxed);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Wait for IO thread to finish
|
|
262
|
-
if (io_thread_.joinable()) {
|
|
263
|
-
LOG_CLIENT_DEBUG("Waiting for IO thread to finish");
|
|
264
|
-
io_thread_.join();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Wait for management thread to finish
|
|
268
|
-
if (management_thread_.joinable()) {
|
|
269
|
-
LOG_CLIENT_DEBUG("Waiting for management thread to finish");
|
|
270
|
-
management_thread_.join();
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Destroy poller after threads have stopped
|
|
274
|
-
poller_.reset();
|
|
275
|
-
|
|
276
|
-
// Join all managed threads for graceful cleanup
|
|
277
|
-
join_all_active_threads();
|
|
278
|
-
|
|
279
|
-
cleanup_socket_library();
|
|
280
|
-
|
|
281
|
-
// Save configuration before stopping
|
|
282
|
-
save_configuration();
|
|
283
|
-
|
|
284
|
-
LOG_CLIENT_INFO("RatsClient stopped successfully");
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
void RatsClient::shutdown_all_threads() {
|
|
288
|
-
LOG_CLIENT_INFO("Initiating shutdown of all background threads");
|
|
289
|
-
|
|
290
|
-
// Signal all threads to stop
|
|
291
|
-
running_.store(false);
|
|
292
|
-
|
|
293
|
-
// Call parent class to handle thread management shutdown
|
|
294
|
-
ThreadManager::shutdown_all_threads();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
bool RatsClient::is_running() const {
|
|
298
|
-
return running_.load();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// =========================================================================
|
|
302
|
-
// Utility Methods
|
|
303
|
-
// =========================================================================
|
|
304
|
-
|
|
305
|
-
int RatsClient::get_listen_port() const {
|
|
306
|
-
return listen_port_;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
std::string RatsClient::get_bind_address() const {
|
|
310
|
-
return bind_address_;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// =========================================================================
|
|
314
|
-
// Async I/O – single-threaded event loop
|
|
315
|
-
// =========================================================================
|
|
316
|
-
|
|
317
|
-
void RatsClient::io_loop() {
|
|
318
|
-
LOG_CLIENT_INFO("IO loop started (backend: " << poller_->name() << ")");
|
|
319
|
-
|
|
320
|
-
static constexpr int MAX_EVENTS = 256;
|
|
321
|
-
PollResult results[MAX_EVENTS];
|
|
322
|
-
|
|
323
|
-
while (running_.load()) {
|
|
324
|
-
int n = poller_->wait(results, MAX_EVENTS, IO_POLL_TIMEOUT_MS);
|
|
325
|
-
|
|
326
|
-
if (n < 0) {
|
|
327
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Collect sockets to disconnect (defer to avoid iterator issues)
|
|
332
|
-
std::vector<socket_t> to_disconnect;
|
|
333
|
-
|
|
334
|
-
for (int i = 0; i < n; ++i) {
|
|
335
|
-
socket_t fd = results[i].fd;
|
|
336
|
-
uint32_t events = results[i].events;
|
|
337
|
-
|
|
338
|
-
// Server socket – accept incoming connections
|
|
339
|
-
if (fd == server_socket_) {
|
|
340
|
-
if (events & PollIn) accept_incoming();
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
bool should_close = false;
|
|
345
|
-
|
|
346
|
-
if (events & (PollErr | PollHup)) {
|
|
347
|
-
should_close = true;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (!should_close && (events & PollIn)) {
|
|
351
|
-
should_close = handle_readable(fd);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (!should_close && (events & PollOut)) {
|
|
355
|
-
should_close = handle_writable(fd);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (should_close) {
|
|
359
|
-
to_disconnect.push_back(fd);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Handle disconnections outside the event loop
|
|
364
|
-
for (socket_t fd : to_disconnect) {
|
|
365
|
-
handle_disconnect(fd);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
LOG_CLIENT_INFO("IO loop ended");
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ---------------------------------------------------------------------------
|
|
373
|
-
// accept_incoming – non-blocking accept of new TCP connections
|
|
374
|
-
// ---------------------------------------------------------------------------
|
|
375
|
-
void RatsClient::accept_incoming() {
|
|
376
|
-
// Accept as many pending connections as possible (level-triggered)
|
|
377
|
-
while (true) {
|
|
378
|
-
socket_t client = accept_client(server_socket_);
|
|
379
|
-
if (!is_valid_socket(client)) break;
|
|
380
|
-
|
|
381
|
-
std::string peer_address = get_peer_address(client);
|
|
382
|
-
if (peer_address.empty()) {
|
|
383
|
-
close_socket(client);
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
std::string ip;
|
|
388
|
-
int port = 0;
|
|
389
|
-
if (!parse_address_string(peer_address, ip, port)) {
|
|
390
|
-
close_socket(client);
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
std::string normalized = normalize_peer_address(ip, port);
|
|
395
|
-
|
|
396
|
-
if (is_peer_limit_reached()) {
|
|
397
|
-
LOG_SERVER_INFO("Peer limit reached, rejecting " << normalized);
|
|
398
|
-
close_socket(client);
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (is_already_connected_to_address(normalized)) {
|
|
403
|
-
LOG_SERVER_DEBUG("Duplicate connection from " << normalized);
|
|
404
|
-
close_socket(client);
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Make the new socket non-blocking and register with poller
|
|
409
|
-
set_socket_nonblocking(client);
|
|
410
|
-
|
|
411
|
-
std::string initial_id = generate_temporary_peer_id(client, "incoming_from_" + peer_address);
|
|
412
|
-
|
|
413
|
-
{
|
|
414
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
415
|
-
RatsPeer new_peer(initial_id, ip, port, client, normalized, false);
|
|
416
|
-
new_peer.encryption_enabled = is_encryption_enabled();
|
|
417
|
-
add_peer_unlocked(new_peer);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
poller_add(client, PollIn);
|
|
421
|
-
|
|
422
|
-
LOG_SERVER_INFO("Accepted incoming connection from " << normalized << " (id: " << initial_id.substr(0, 8) << "…)");
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ---------------------------------------------------------------------------
|
|
427
|
-
// handle_readable – drain kernel buffer, parse length-prefixed frames
|
|
428
|
-
// Returns true if the peer should be disconnected.
|
|
429
|
-
// ---------------------------------------------------------------------------
|
|
430
|
-
bool RatsClient::handle_readable(socket_t socket) {
|
|
431
|
-
// ── Phase 1: drain data & extract complete frames under lock ──────────
|
|
432
|
-
struct PendingFrame {
|
|
433
|
-
std::vector<uint8_t> data;
|
|
434
|
-
RatsPeer::HandshakeState state;
|
|
435
|
-
std::string peer_id;
|
|
436
|
-
bool noise_encrypted;
|
|
437
|
-
std::shared_ptr<rats::NoiseCipherState> recv_cipher;
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
std::vector<PendingFrame> frames;
|
|
441
|
-
bool peer_closed = false;
|
|
442
|
-
bool need_post_handshake = false;
|
|
443
|
-
RatsPeer peer_copy_for_post_handshake;
|
|
444
|
-
|
|
445
|
-
{
|
|
446
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
447
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
448
|
-
if (peer_it == peers_.end()) return true;
|
|
449
|
-
|
|
450
|
-
RatsPeer& peer = peer_it->second;
|
|
451
|
-
auto& recv_buf = peer.io_.recv_buffer;
|
|
452
|
-
|
|
453
|
-
// Non-blocking recv loop
|
|
454
|
-
while (true) {
|
|
455
|
-
recv_buf.ensure_space(16384);
|
|
456
|
-
int bytes = ::recv(socket,
|
|
457
|
-
reinterpret_cast<char*>(recv_buf.write_ptr()),
|
|
458
|
-
static_cast<int>(recv_buf.write_space()), 0);
|
|
459
|
-
|
|
460
|
-
if (bytes > 0) {
|
|
461
|
-
recv_buf.received(static_cast<size_t>(bytes));
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
if (bytes == 0) { peer_closed = true; break; }
|
|
465
|
-
|
|
466
|
-
#ifdef _WIN32
|
|
467
|
-
if (WSAGetLastError() == WSAEWOULDBLOCK) break;
|
|
468
|
-
#else
|
|
469
|
-
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
470
|
-
#endif
|
|
471
|
-
peer_closed = true;
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (peer_closed && recv_buf.empty()) return true;
|
|
476
|
-
|
|
477
|
-
// Parse length-prefixed frames: [4-byte network-order length][payload]
|
|
478
|
-
while (recv_buf.size() >= 4) {
|
|
479
|
-
uint32_t net_len;
|
|
480
|
-
memcpy(&net_len, recv_buf.data(), 4);
|
|
481
|
-
uint32_t msg_len = ntohl(net_len);
|
|
482
|
-
|
|
483
|
-
if (msg_len > MAX_FRAME_SIZE) {
|
|
484
|
-
LOG_CLIENT_ERROR("Frame too large (" << msg_len << " bytes) from " << peer.peer_id);
|
|
485
|
-
return true;
|
|
486
|
-
}
|
|
487
|
-
if (recv_buf.size() < 4 + msg_len) break; // incomplete frame
|
|
488
|
-
|
|
489
|
-
// Handle Noise handshake messages inline (fast crypto, no callbacks)
|
|
490
|
-
if (peer.handshake_state == RatsPeer::HandshakeState::NOISE_PENDING) {
|
|
491
|
-
if (!handle_noise_frame(peer)) return true;
|
|
492
|
-
recv_buf.consume(4 + msg_len);
|
|
493
|
-
|
|
494
|
-
// Check if Noise just completed
|
|
495
|
-
if (peer.handshake_state == RatsPeer::HandshakeState::COMPLETED) {
|
|
496
|
-
need_post_handshake = true;
|
|
497
|
-
peer_copy_for_post_handshake = peer;
|
|
498
|
-
}
|
|
499
|
-
continue;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Snapshot state for out-of-lock processing
|
|
503
|
-
PendingFrame pf;
|
|
504
|
-
pf.data.assign(recv_buf.data() + 4, recv_buf.data() + 4 + msg_len);
|
|
505
|
-
pf.state = peer.handshake_state;
|
|
506
|
-
pf.peer_id = peer.peer_id;
|
|
507
|
-
pf.noise_encrypted = peer.is_noise_encrypted();
|
|
508
|
-
if (pf.noise_encrypted) pf.recv_cipher = peer.recv_cipher;
|
|
509
|
-
|
|
510
|
-
frames.push_back(std::move(pf));
|
|
511
|
-
recv_buf.consume(4 + msg_len);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// Compact if >32KB wasted at front
|
|
515
|
-
if (recv_buf.front_waste() > 32768) recv_buf.normalize();
|
|
516
|
-
}
|
|
517
|
-
// ── peers_mutex_ released ────────────────────────────────────────────
|
|
518
|
-
|
|
519
|
-
// Deferred post-handshake completion (includes callbacks – must be outside mutex)
|
|
520
|
-
if (need_post_handshake) {
|
|
521
|
-
handle_post_handshake_completion(socket, peer_copy_for_post_handshake);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// ── Phase 2: process frames outside lock ─────────────────────────────
|
|
525
|
-
for (auto& pf : frames) {
|
|
526
|
-
// Handshake phase – RATS JSON handshake messages
|
|
527
|
-
if (pf.state != RatsPeer::HandshakeState::COMPLETED) {
|
|
528
|
-
if (is_handshake_message(pf.data)) {
|
|
529
|
-
if (!handle_handshake_message(socket, pf.peer_id, pf.data)) {
|
|
530
|
-
return true;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Check if handshake just completed and handle Noise / post-handshake
|
|
534
|
-
bool do_post_handshake = false;
|
|
535
|
-
RatsPeer post_hs_copy;
|
|
536
|
-
{
|
|
537
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
538
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
539
|
-
if (peer_it == peers_.end()) return true;
|
|
540
|
-
RatsPeer& peer = peer_it->second;
|
|
541
|
-
|
|
542
|
-
if (peer.handshake_state == RatsPeer::HandshakeState::NOISE_PENDING) {
|
|
543
|
-
start_noise_handshake_async(peer);
|
|
544
|
-
} else if (peer.handshake_state == RatsPeer::HandshakeState::COMPLETED) {
|
|
545
|
-
post_hs_copy = peer;
|
|
546
|
-
do_post_handshake = true;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
// peers_mutex_ released – safe to invoke callbacks
|
|
550
|
-
if (do_post_handshake) {
|
|
551
|
-
handle_post_handshake_completion(socket, post_hs_copy);
|
|
552
|
-
}
|
|
553
|
-
} else {
|
|
554
|
-
LOG_CLIENT_WARN("Non-handshake data from " << pf.peer_id << " before handshake – ignoring");
|
|
555
|
-
}
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Data phase – decrypt if needed, then dispatch
|
|
560
|
-
std::vector<uint8_t> plaintext;
|
|
561
|
-
if (pf.noise_encrypted && pf.recv_cipher) {
|
|
562
|
-
if (pf.data.size() < rats::NOISE_TAG_SIZE) {
|
|
563
|
-
LOG_CLIENT_ERROR("Encrypted frame too small from " << pf.peer_id);
|
|
564
|
-
return true;
|
|
565
|
-
}
|
|
566
|
-
plaintext.resize(pf.data.size());
|
|
567
|
-
size_t pt_len = pf.recv_cipher->decrypt_with_ad(
|
|
568
|
-
nullptr, 0, pf.data.data(), pf.data.size(), plaintext.data());
|
|
569
|
-
if (pt_len == 0) {
|
|
570
|
-
LOG_CLIENT_ERROR("Decryption failed from " << pf.peer_id);
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
plaintext.resize(pt_len);
|
|
574
|
-
} else {
|
|
575
|
-
plaintext = std::move(pf.data);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
process_message(socket, plaintext, pf.peer_id);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return peer_closed;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// ---------------------------------------------------------------------------
|
|
585
|
-
// handle_writable – flush the peer's send buffer to the kernel
|
|
586
|
-
// Returns true if the peer should be disconnected.
|
|
587
|
-
// ---------------------------------------------------------------------------
|
|
588
|
-
bool RatsClient::handle_writable(socket_t socket) {
|
|
589
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
590
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
591
|
-
if (peer_it == peers_.end()) return true;
|
|
592
|
-
|
|
593
|
-
auto& send_buf = peer_it->second.io_.send_buffer;
|
|
594
|
-
|
|
595
|
-
while (!send_buf.empty()) {
|
|
596
|
-
int bytes = ::send(socket,
|
|
597
|
-
reinterpret_cast<const char*>(send_buf.front_data()),
|
|
598
|
-
static_cast<int>(send_buf.front_size()),
|
|
599
|
-
#ifdef _WIN32
|
|
600
|
-
0
|
|
601
|
-
#else
|
|
602
|
-
MSG_NOSIGNAL
|
|
603
|
-
#endif
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
if (bytes > 0) {
|
|
607
|
-
send_buf.pop_front(static_cast<size_t>(bytes));
|
|
608
|
-
continue;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
if (bytes < 0) {
|
|
612
|
-
#ifdef _WIN32
|
|
613
|
-
if (WSAGetLastError() == WSAEWOULDBLOCK) break;
|
|
614
|
-
#else
|
|
615
|
-
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
616
|
-
#endif
|
|
617
|
-
LOG_CLIENT_ERROR("Send error on socket " << socket);
|
|
618
|
-
return true;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// bytes == 0 — shouldn't happen on a stream socket
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// If buffer fully flushed, stop watching for PollOut
|
|
626
|
-
if (send_buf.empty()) {
|
|
627
|
-
std::lock_guard<std::mutex> io_lock(io_mutex_);
|
|
628
|
-
if (poller_) poller_->modify(socket, PollIn);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// ---------------------------------------------------------------------------
|
|
635
|
-
// handle_disconnect – clean up peer on error / hangup / close
|
|
636
|
-
// ---------------------------------------------------------------------------
|
|
637
|
-
void RatsClient::handle_disconnect(socket_t socket) {
|
|
638
|
-
// Gather info before removing
|
|
639
|
-
std::string peer_id;
|
|
640
|
-
bool was_validated = false;
|
|
641
|
-
RatsPeer peer_copy_for_reconnect;
|
|
642
|
-
bool should_schedule_reconnect = false;
|
|
643
|
-
|
|
644
|
-
{
|
|
645
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
646
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
647
|
-
if (peer_it == peers_.end()) {
|
|
648
|
-
// Already removed
|
|
649
|
-
poller_remove(socket);
|
|
650
|
-
close_socket(socket);
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
peer_id = peer_it->second.peer_id;
|
|
654
|
-
was_validated = peer_it->second.is_handshake_completed();
|
|
655
|
-
if (was_validated) {
|
|
656
|
-
peer_copy_for_reconnect = peer_it->second;
|
|
657
|
-
should_schedule_reconnect = true;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
poller_remove(socket);
|
|
662
|
-
remove_peer(socket);
|
|
663
|
-
|
|
664
|
-
if (was_validated) {
|
|
665
|
-
if (disconnect_callback_) {
|
|
666
|
-
disconnect_callback_(socket, peer_id);
|
|
667
|
-
}
|
|
668
|
-
if (gossipsub_) {
|
|
669
|
-
gossipsub_->handle_peer_disconnected(peer_id);
|
|
670
|
-
}
|
|
671
|
-
if (file_transfer_manager_) {
|
|
672
|
-
file_transfer_manager_->on_peer_disconnected(peer_id);
|
|
673
|
-
}
|
|
674
|
-
if (should_schedule_reconnect && running_.load()) {
|
|
675
|
-
schedule_reconnect(peer_copy_for_reconnect);
|
|
676
|
-
}
|
|
677
|
-
if (running_.load()) {
|
|
678
|
-
add_managed_thread(std::thread([this]() {
|
|
679
|
-
if (running_.load()) save_configuration();
|
|
680
|
-
}), "config-save-disconnect");
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
close_socket(socket);
|
|
685
|
-
|
|
686
|
-
LOG_CLIENT_INFO("Peer disconnected: " << peer_id);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// ---------------------------------------------------------------------------
|
|
690
|
-
// Poller registration helpers (thread-safe via io_mutex_)
|
|
691
|
-
// ---------------------------------------------------------------------------
|
|
692
|
-
void RatsClient::poller_add(socket_t fd, uint32_t events) {
|
|
693
|
-
std::lock_guard<std::mutex> lock(io_mutex_);
|
|
694
|
-
if (poller_) poller_->add(fd, events);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
void RatsClient::poller_modify(socket_t fd, uint32_t events) {
|
|
698
|
-
std::lock_guard<std::mutex> lock(io_mutex_);
|
|
699
|
-
if (poller_) poller_->modify(fd, events);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
void RatsClient::poller_remove(socket_t fd) {
|
|
703
|
-
std::lock_guard<std::mutex> lock(io_mutex_);
|
|
704
|
-
if (poller_) poller_->remove(fd);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// ---------------------------------------------------------------------------
|
|
708
|
-
// enqueue_message – build a length-prefixed frame and append to send buffer
|
|
709
|
-
// ---------------------------------------------------------------------------
|
|
710
|
-
bool RatsClient::enqueue_message(socket_t socket, const std::vector<uint8_t>& data) {
|
|
711
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
712
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
713
|
-
if (peer_it == peers_.end()) return false;
|
|
714
|
-
return enqueue_message_unlocked(peer_it->second, data);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
bool RatsClient::enqueue_message_unlocked(RatsPeer& peer, const std::vector<uint8_t>& data) {
|
|
718
|
-
// Build length-prefixed frame: [4-byte network-order length][payload]
|
|
719
|
-
uint32_t net_len = htonl(static_cast<uint32_t>(data.size()));
|
|
720
|
-
|
|
721
|
-
std::vector<uint8_t> frame;
|
|
722
|
-
frame.reserve(4 + data.size());
|
|
723
|
-
frame.insert(frame.end(),
|
|
724
|
-
reinterpret_cast<const uint8_t*>(&net_len),
|
|
725
|
-
reinterpret_cast<const uint8_t*>(&net_len) + 4);
|
|
726
|
-
frame.insert(frame.end(), data.begin(), data.end());
|
|
727
|
-
|
|
728
|
-
peer.io_.send_buffer.append(std::move(frame));
|
|
729
|
-
|
|
730
|
-
// Arm PollOut so io_loop flushes the buffer
|
|
731
|
-
{
|
|
732
|
-
std::lock_guard<std::mutex> io_lock(io_mutex_);
|
|
733
|
-
if (poller_) poller_->modify(peer.socket, PollIn | PollOut);
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return true;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
void RatsClient::management_loop() {
|
|
740
|
-
LOG_CLIENT_INFO("Management loop started");
|
|
741
|
-
|
|
742
|
-
auto last_thread_cleanup = std::chrono::steady_clock::now();
|
|
743
|
-
const auto thread_cleanup_interval = std::chrono::seconds(THREAD_CLEANUP_INTERVAL_SECONDS);
|
|
744
|
-
|
|
745
|
-
while (running_.load()) {
|
|
746
|
-
// Wait for interval or until shutdown (for responsive reconnection processing)
|
|
747
|
-
{
|
|
748
|
-
std::unique_lock<std::mutex> lock(shutdown_mutex_);
|
|
749
|
-
if (shutdown_cv_.wait_for(lock, std::chrono::seconds(MANAGEMENT_LOOP_INTERVAL_SECONDS), [this] { return !running_.load(); })) {
|
|
750
|
-
break; // Exit if shutdown requested
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// Check handshake timeouts (centralized, runs once for all peers)
|
|
755
|
-
try {
|
|
756
|
-
check_handshake_timeouts();
|
|
757
|
-
} catch (const std::exception& e) {
|
|
758
|
-
LOG_CLIENT_ERROR("Exception during handshake timeout check: " << e.what());
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// Process reconnection queue
|
|
762
|
-
try {
|
|
763
|
-
process_reconnect_queue();
|
|
764
|
-
} catch (const std::exception& e) {
|
|
765
|
-
LOG_CLIENT_ERROR("Exception during reconnect queue processing: " << e.what());
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// Periodically cleanup finished threads (every 30 seconds)
|
|
769
|
-
auto now = std::chrono::steady_clock::now();
|
|
770
|
-
if (now - last_thread_cleanup >= thread_cleanup_interval) {
|
|
771
|
-
try {
|
|
772
|
-
cleanup_finished_threads();
|
|
773
|
-
LOG_CLIENT_DEBUG("Periodic thread cleanup completed. Active threads: " << get_active_thread_count());
|
|
774
|
-
} catch (const std::exception& e) {
|
|
775
|
-
LOG_CLIENT_ERROR("Exception during thread cleanup: " << e.what());
|
|
776
|
-
}
|
|
777
|
-
last_thread_cleanup = now;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
LOG_CLIENT_INFO("Management loop ended");
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
void RatsClient::handle_post_handshake_completion(socket_t socket, const RatsPeer& peer_copy) {
|
|
785
|
-
// Remove from reconnection queue (successful connection)
|
|
786
|
-
remove_from_reconnect_queue(peer_copy.peer_id);
|
|
787
|
-
|
|
788
|
-
// Connection callback
|
|
789
|
-
if (connection_callback_) {
|
|
790
|
-
connection_callback_(socket, peer_copy.peer_id);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// GossipSub notification
|
|
794
|
-
if (gossipsub_) {
|
|
795
|
-
gossipsub_->handle_peer_connected(peer_copy.peer_id);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
#ifdef RATS_STORAGE
|
|
799
|
-
// Storage manager notification
|
|
800
|
-
if (storage_manager_) {
|
|
801
|
-
storage_manager_->on_peer_connected(peer_copy.peer_id);
|
|
802
|
-
}
|
|
803
|
-
#endif
|
|
804
|
-
|
|
805
|
-
// Peer exchange broadcast
|
|
806
|
-
broadcast_peer_exchange_message(peer_copy);
|
|
807
|
-
|
|
808
|
-
// Request peers from newly connected peer (outgoing only)
|
|
809
|
-
if (peer_copy.is_outgoing) {
|
|
810
|
-
send_peers_request(socket, peer_copy.peer_id);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// Save configuration
|
|
814
|
-
if (running_.load()) {
|
|
815
|
-
add_managed_thread(std::thread([this]() {
|
|
816
|
-
if (running_.load()) {
|
|
817
|
-
save_configuration();
|
|
818
|
-
}
|
|
819
|
-
}), "config-save");
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
void RatsClient::process_message(socket_t socket, const std::vector<uint8_t>& data, const std::string& initial_peer_id) {
|
|
824
|
-
MessageHeader header;
|
|
825
|
-
std::vector<uint8_t> payload;
|
|
826
|
-
|
|
827
|
-
if (!parse_message_with_header(data, header, payload)) {
|
|
828
|
-
LOG_CLIENT_WARN("No header found in message from " << initial_peer_id);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
std::string peer_id = get_peer_id(socket);
|
|
833
|
-
|
|
834
|
-
switch (header.type) {
|
|
835
|
-
case MessageDataType::BINARY: {
|
|
836
|
-
LOG_CLIENT_DEBUG("Received BINARY message from " << peer_id << " (payload size: " << payload.size() << ")");
|
|
837
|
-
bool handled = false;
|
|
838
|
-
if (file_transfer_manager_) {
|
|
839
|
-
handled = file_transfer_manager_->handle_binary_data(peer_id, payload);
|
|
840
|
-
}
|
|
841
|
-
if (!handled && binary_data_callback_) {
|
|
842
|
-
binary_data_callback_(socket, peer_id, payload);
|
|
843
|
-
}
|
|
844
|
-
break;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
case MessageDataType::STRING: {
|
|
848
|
-
LOG_CLIENT_DEBUG("Received STRING message from " << peer_id << " (payload size: " << payload.size() << ")");
|
|
849
|
-
if (string_data_callback_) {
|
|
850
|
-
std::string string_data(payload.begin(), payload.end());
|
|
851
|
-
string_data_callback_(socket, peer_id, string_data);
|
|
852
|
-
}
|
|
853
|
-
break;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
case MessageDataType::JSON: {
|
|
857
|
-
LOG_CLIENT_DEBUG("Received JSON message from " << peer_id << " (payload size: " << payload.size() << ")");
|
|
858
|
-
try {
|
|
859
|
-
nlohmann::json json_msg = nlohmann::json::parse(payload.begin(), payload.end());
|
|
860
|
-
if (json_msg.contains("rats_protocol") && json_msg["rats_protocol"] == true) {
|
|
861
|
-
handle_rats_message(socket, peer_id, json_msg);
|
|
862
|
-
} else if (json_data_callback_) {
|
|
863
|
-
json_data_callback_(socket, peer_id, json_msg);
|
|
864
|
-
}
|
|
865
|
-
} catch (const nlohmann::json::exception& e) {
|
|
866
|
-
LOG_CLIENT_ERROR("Received invalid JSON in JSON message from " << peer_id << ": " << e.what());
|
|
867
|
-
}
|
|
868
|
-
break;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
default:
|
|
872
|
-
LOG_CLIENT_WARN("Received message with unknown data type " << static_cast<int>(header.type) << " from " << peer_id);
|
|
873
|
-
break;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// Handshake protocol implementation
|
|
878
|
-
std::string RatsClient::create_handshake_message(const std::string& message_type, const std::string& our_peer_id) const {
|
|
879
|
-
auto now = std::chrono::high_resolution_clock::now();
|
|
880
|
-
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
|
881
|
-
|
|
882
|
-
// Use nlohmann::json for proper JSON serialization
|
|
883
|
-
nlohmann::json handshake_msg;
|
|
884
|
-
{
|
|
885
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
886
|
-
handshake_msg["protocol"] = custom_protocol_name_;
|
|
887
|
-
handshake_msg["version"] = custom_protocol_version_;
|
|
888
|
-
}
|
|
889
|
-
handshake_msg["peer_id"] = our_peer_id;
|
|
890
|
-
handshake_msg["message_type"] = message_type;
|
|
891
|
-
handshake_msg["timestamp"] = timestamp;
|
|
892
|
-
handshake_msg["encryption_enabled"] = is_encryption_enabled();
|
|
893
|
-
handshake_msg["listen_port"] = listen_port_;
|
|
894
|
-
|
|
895
|
-
return handshake_msg.dump();
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
bool RatsClient::parse_handshake_message(const std::vector<uint8_t>& data, HandshakeMessage& out_msg) const {
|
|
899
|
-
try {
|
|
900
|
-
// Use nlohmann::json with iterators to avoid string conversion
|
|
901
|
-
nlohmann::json json_msg = nlohmann::json::parse(data.begin(), data.end());
|
|
902
|
-
|
|
903
|
-
// Clear the output structure
|
|
904
|
-
out_msg = HandshakeMessage{};
|
|
905
|
-
|
|
906
|
-
// Extract fields using nlohmann::json
|
|
907
|
-
out_msg.protocol = json_msg.value("protocol", "");
|
|
908
|
-
out_msg.version = json_msg.value("version", "");
|
|
909
|
-
out_msg.peer_id = json_msg.value("peer_id", "");
|
|
910
|
-
out_msg.message_type = json_msg.value("message_type", "");
|
|
911
|
-
// Tolerate missing timestamp to avoid hard dependency on remote system clock
|
|
912
|
-
out_msg.timestamp = json_msg.value("timestamp", static_cast<int64_t>(0));
|
|
913
|
-
// Parse encryption_enabled (default to false for backward compatibility)
|
|
914
|
-
out_msg.encryption_enabled = json_msg.value("encryption_enabled", false);
|
|
915
|
-
// Parse listen_port (default to 0 for backward compatibility with older clients)
|
|
916
|
-
out_msg.listen_port = json_msg.value("listen_port", static_cast<uint16_t>(0));
|
|
917
|
-
|
|
918
|
-
return true;
|
|
919
|
-
|
|
920
|
-
} catch (const nlohmann::json::exception& e) {
|
|
921
|
-
LOG_CLIENT_ERROR("Failed to parse handshake message: " << e.what());
|
|
922
|
-
return false;
|
|
923
|
-
} catch (const std::exception& e) {
|
|
924
|
-
LOG_CLIENT_ERROR("Failed to parse handshake message: " << e.what());
|
|
925
|
-
return false;
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
bool RatsClient::validate_handshake_message(const HandshakeMessage& msg) const {
|
|
930
|
-
std::string expected_protocol;
|
|
931
|
-
std::string expected_version;
|
|
932
|
-
{
|
|
933
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
934
|
-
expected_protocol = custom_protocol_name_;
|
|
935
|
-
expected_version = custom_protocol_version_;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
// Validate protocol
|
|
939
|
-
if (msg.protocol != expected_protocol) {
|
|
940
|
-
LOG_CLIENT_WARN("Invalid handshake protocol: " << msg.protocol << " (expected: " << expected_protocol << ")");
|
|
941
|
-
return false;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// Validate version (for now, only accept exact version match)
|
|
945
|
-
if (msg.version != expected_version) {
|
|
946
|
-
LOG_CLIENT_WARN("Unsupported protocol version: " << msg.version << " (expected: " << expected_version << ")");
|
|
947
|
-
return false;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// Validate message type
|
|
951
|
-
if (msg.message_type != "handshake") {
|
|
952
|
-
LOG_CLIENT_WARN("Invalid handshake message type: " << msg.message_type);
|
|
953
|
-
return false;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// Validate peer_id (must not be empty)
|
|
957
|
-
if (msg.peer_id.empty()) {
|
|
958
|
-
LOG_CLIENT_WARN("Empty peer_id in handshake message");
|
|
959
|
-
return false;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
// Soft-validate timestamp to avoid rejecting valid peers due to clock skew
|
|
963
|
-
if (msg.timestamp == 0) {
|
|
964
|
-
LOG_CLIENT_WARN("Handshake missing timestamp; accepting to avoid clock-skew rejection");
|
|
965
|
-
} else {
|
|
966
|
-
auto now = std::chrono::high_resolution_clock::now();
|
|
967
|
-
auto current_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
|
968
|
-
int64_t time_diff = std::abs(current_timestamp - msg.timestamp);
|
|
969
|
-
if (time_diff > TIMESTAMP_SKEW_TOLERANCE_MS) {
|
|
970
|
-
LOG_CLIENT_WARN("Handshake timestamp skew " << time_diff << "ms exceeds " << TIMESTAMP_SKEW_TOLERANCE_MS << "ms; accepting to be tolerant of clock skew");
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
return true;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
bool RatsClient::is_handshake_message(const std::vector<uint8_t>& data) const {
|
|
978
|
-
try {
|
|
979
|
-
// Check if message has our message header (starts with "RATS" magic)
|
|
980
|
-
MessageHeader header;
|
|
981
|
-
std::vector<uint8_t> payload;
|
|
982
|
-
|
|
983
|
-
if (parse_message_with_header(data, header, payload)) {
|
|
984
|
-
// Message has valid header - extract the JSON payload
|
|
985
|
-
if (header.type == MessageDataType::STRING || header.type == MessageDataType::JSON) {
|
|
986
|
-
// Parse the JSON message directly from payload
|
|
987
|
-
nlohmann::json json_msg = nlohmann::json::parse(payload.begin(), payload.end());
|
|
988
|
-
std::string expected_protocol;
|
|
989
|
-
{
|
|
990
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
991
|
-
expected_protocol = custom_protocol_name_;
|
|
992
|
-
}
|
|
993
|
-
return json_msg.value("protocol", "") == expected_protocol &&
|
|
994
|
-
json_msg.value("message_type", "") == "handshake";
|
|
995
|
-
}
|
|
996
|
-
// Handshake messages should be string/JSON type
|
|
997
|
-
return false;
|
|
998
|
-
}
|
|
999
|
-
// Message has no header
|
|
1000
|
-
return false;
|
|
1001
|
-
} catch (const std::exception&) {
|
|
1002
|
-
return false;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
bool RatsClient::send_handshake_unlocked(RatsPeer& peer, const std::string& our_peer_id) {
|
|
1007
|
-
std::string handshake_msg = create_handshake_message("handshake", our_peer_id);
|
|
1008
|
-
LOG_CLIENT_DEBUG("Sending handshake to " << peer.peer_id << ": " << handshake_msg);
|
|
1009
|
-
|
|
1010
|
-
// Handshakes are always unencrypted – enqueue into the peer's send buffer
|
|
1011
|
-
std::vector<uint8_t> binary_data(handshake_msg.begin(), handshake_msg.end());
|
|
1012
|
-
std::vector<uint8_t> message_with_header = create_message_with_header(binary_data, MessageDataType::STRING);
|
|
1013
|
-
|
|
1014
|
-
if (!enqueue_message_unlocked(peer, message_with_header)) {
|
|
1015
|
-
LOG_CLIENT_ERROR("Failed to enqueue handshake for " << peer.peer_id);
|
|
1016
|
-
return false;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
peer.handshake_state = RatsPeer::HandshakeState::SENT;
|
|
1020
|
-
peer.handshake_start_time = std::chrono::steady_clock::now();
|
|
1021
|
-
|
|
1022
|
-
return true;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
bool RatsClient::handle_handshake_message(socket_t socket, const std::string& initial_peer_id, const std::vector<uint8_t>& data) {
|
|
1026
|
-
// Extract JSON payload from message header
|
|
1027
|
-
MessageHeader header;
|
|
1028
|
-
std::vector<uint8_t> payload;
|
|
1029
|
-
|
|
1030
|
-
if (!parse_message_with_header(data, header, payload)) {
|
|
1031
|
-
LOG_CLIENT_ERROR("Failed to parse handshake message header from " << initial_peer_id);
|
|
1032
|
-
return false;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// Message has valid header - check the type
|
|
1036
|
-
if (header.type != MessageDataType::STRING && header.type != MessageDataType::JSON) {
|
|
1037
|
-
LOG_CLIENT_ERROR("Invalid message type for handshake: " << static_cast<int>(header.type));
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// Parse handshake message directly from payload (no string conversion)
|
|
1042
|
-
HandshakeMessage handshake_msg;
|
|
1043
|
-
if (!parse_handshake_message(payload, handshake_msg)) {
|
|
1044
|
-
LOG_CLIENT_ERROR("Failed to parse handshake message from " << initial_peer_id);
|
|
1045
|
-
return false;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
if (!validate_handshake_message(handshake_msg)) {
|
|
1049
|
-
LOG_CLIENT_ERROR("Invalid handshake message from " << initial_peer_id);
|
|
1050
|
-
return false;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
if (handshake_msg.peer_id == get_our_peer_id()) {
|
|
1054
|
-
LOG_CLIENT_INFO("Received handshake from ourselves, ignoring");
|
|
1055
|
-
return false;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
LOG_CLIENT_INFO("Received valid handshake from " << initial_peer_id
|
|
1059
|
-
<< " (peer_id: " << handshake_msg.peer_id << ")");
|
|
1060
|
-
|
|
1061
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1062
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
1063
|
-
if (peer_it == peers_.end()) {
|
|
1064
|
-
LOG_CLIENT_ERROR("Peer " << initial_peer_id << " not found for socket " << socket);
|
|
1065
|
-
return false;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
if (peers_.find(handshake_msg.peer_id) != peers_.end()) {
|
|
1069
|
-
LOG_CLIENT_INFO("Peer " << handshake_msg.peer_id << " already connected, closing duplicate connection");
|
|
1070
|
-
// This is a duplicate connection - the existing connection should remain stable
|
|
1071
|
-
// Return false to close this duplicate connection, but this is expected behavior
|
|
1072
|
-
return false;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// Store old peer ID for mapping updates
|
|
1076
|
-
std::string old_peer_id = peer_it->second.peer_id;
|
|
1077
|
-
|
|
1078
|
-
// Update peer mappings with new peer_id if it changed
|
|
1079
|
-
if (old_peer_id != handshake_msg.peer_id) {
|
|
1080
|
-
// Move the peer (preserves io_ context with send/recv buffers)
|
|
1081
|
-
RatsPeer peer_moved = std::move(peer_it->second);
|
|
1082
|
-
peers_.erase(peer_it);
|
|
1083
|
-
|
|
1084
|
-
peer_moved.peer_id = handshake_msg.peer_id;
|
|
1085
|
-
|
|
1086
|
-
// Use emplace to avoid extra copy/move
|
|
1087
|
-
auto [new_it, ok] = peers_.emplace(peer_moved.peer_id, std::move(peer_moved));
|
|
1088
|
-
socket_to_peer_id_[socket] = new_it->second.peer_id;
|
|
1089
|
-
address_to_peer_id_[new_it->second.normalized_address] = new_it->second.peer_id;
|
|
1090
|
-
|
|
1091
|
-
peer_it = new_it;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
RatsPeer& peer = peer_it->second;
|
|
1095
|
-
|
|
1096
|
-
// Store remote peer information
|
|
1097
|
-
peer.version = handshake_msg.version;
|
|
1098
|
-
|
|
1099
|
-
// Determine if encryption should be used for this connection
|
|
1100
|
-
// Encryption is enabled only if BOTH sides support it
|
|
1101
|
-
bool local_encryption = is_encryption_enabled();
|
|
1102
|
-
bool remote_encryption = handshake_msg.encryption_enabled;
|
|
1103
|
-
peer.encryption_enabled = local_encryption && remote_encryption;
|
|
1104
|
-
|
|
1105
|
-
LOG_CLIENT_INFO("Encryption negotiation: local=" << local_encryption
|
|
1106
|
-
<< ", remote=" << remote_encryption
|
|
1107
|
-
<< ", result=" << peer.encryption_enabled);
|
|
1108
|
-
|
|
1109
|
-
// For incoming connections, update port to the peer's actual listen port
|
|
1110
|
-
// This is critical for peer exchange to work correctly
|
|
1111
|
-
if (!peer.is_outgoing && handshake_msg.listen_port > 0) {
|
|
1112
|
-
// Remove old address mapping
|
|
1113
|
-
address_to_peer_id_.erase(peer.normalized_address);
|
|
1114
|
-
|
|
1115
|
-
// Update port and normalized address
|
|
1116
|
-
peer.port = handshake_msg.listen_port;
|
|
1117
|
-
peer.normalized_address = normalize_peer_address(peer.ip, peer.port);
|
|
1118
|
-
|
|
1119
|
-
// Add new address mapping
|
|
1120
|
-
address_to_peer_id_[peer.normalized_address] = peer.peer_id;
|
|
1121
|
-
|
|
1122
|
-
LOG_CLIENT_INFO("Updated incoming peer port to listen_port: " << peer.ip << ":" << peer.port);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// Simplified handshake logic - just one message type
|
|
1126
|
-
if (peer.handshake_state == RatsPeer::HandshakeState::PENDING) {
|
|
1127
|
-
// This is an incoming handshake - send our handshake back
|
|
1128
|
-
if (send_handshake_unlocked(peer, get_our_peer_id())) {
|
|
1129
|
-
// If encryption is enabled, we need to do Noise handshake first
|
|
1130
|
-
// Set NOISE_PENDING to prevent other threads from sending messages
|
|
1131
|
-
if (peer.encryption_enabled) {
|
|
1132
|
-
peer.handshake_state = RatsPeer::HandshakeState::NOISE_PENDING;
|
|
1133
|
-
LOG_CLIENT_DEBUG("Rats handshake done, entering NOISE_PENDING state for " << initial_peer_id);
|
|
1134
|
-
} else {
|
|
1135
|
-
peer.handshake_state = RatsPeer::HandshakeState::COMPLETED;
|
|
1136
|
-
validated_peer_count_.fetch_add(1, std::memory_order_relaxed);
|
|
1137
|
-
log_handshake_completion_unlocked(peer);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
// Append to historical peers file after successful connection
|
|
1141
|
-
append_peer_to_historical_file(peer);
|
|
1142
|
-
|
|
1143
|
-
return true;
|
|
1144
|
-
} else {
|
|
1145
|
-
peer.handshake_state = RatsPeer::HandshakeState::FAILED;
|
|
1146
|
-
LOG_CLIENT_ERROR("Failed to send handshake response to " << initial_peer_id);
|
|
1147
|
-
return false;
|
|
1148
|
-
}
|
|
1149
|
-
} else if (peer.handshake_state == RatsPeer::HandshakeState::SENT) {
|
|
1150
|
-
// This is a response to our handshake
|
|
1151
|
-
// If encryption is enabled, we need to do Noise handshake first
|
|
1152
|
-
// Set NOISE_PENDING to prevent other threads from sending messages
|
|
1153
|
-
if (peer.encryption_enabled) {
|
|
1154
|
-
peer.handshake_state = RatsPeer::HandshakeState::NOISE_PENDING;
|
|
1155
|
-
LOG_CLIENT_DEBUG("Rats handshake done, entering NOISE_PENDING state for " << initial_peer_id);
|
|
1156
|
-
} else {
|
|
1157
|
-
peer.handshake_state = RatsPeer::HandshakeState::COMPLETED;
|
|
1158
|
-
validated_peer_count_.fetch_add(1, std::memory_order_relaxed);
|
|
1159
|
-
log_handshake_completion_unlocked(peer);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// Append to historical peers file after successful connection
|
|
1163
|
-
append_peer_to_historical_file(peer);
|
|
1164
|
-
|
|
1165
|
-
return true;
|
|
1166
|
-
} else {
|
|
1167
|
-
LOG_CLIENT_WARN("Received handshake from " << initial_peer_id << " but handshake state is " << static_cast<int>(peer.handshake_state));
|
|
1168
|
-
return false;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
void RatsClient::check_handshake_timeouts() {
|
|
1173
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1174
|
-
auto now = std::chrono::steady_clock::now();
|
|
1175
|
-
|
|
1176
|
-
std::vector<std::string> peers_to_remove;
|
|
1177
|
-
|
|
1178
|
-
for (auto& pair : peers_) {
|
|
1179
|
-
RatsPeer& peer = pair.second;
|
|
1180
|
-
|
|
1181
|
-
if (peer.handshake_state != RatsPeer::HandshakeState::COMPLETED &&
|
|
1182
|
-
peer.handshake_state != RatsPeer::HandshakeState::FAILED) {
|
|
1183
|
-
|
|
1184
|
-
auto handshake_duration = std::chrono::duration_cast<std::chrono::seconds>(now - peer.handshake_start_time);
|
|
1185
|
-
|
|
1186
|
-
if (handshake_duration.count() > HANDSHAKE_TIMEOUT_SECONDS) {
|
|
1187
|
-
LOG_CLIENT_WARN("Handshake timeout for peer " << peer.peer_id << " after " << handshake_duration.count() << " seconds");
|
|
1188
|
-
peer.handshake_state = RatsPeer::HandshakeState::FAILED;
|
|
1189
|
-
peers_to_remove.push_back(peer.peer_id);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
// Remove timed out peers
|
|
1195
|
-
for (const auto& peer_id : peers_to_remove) {
|
|
1196
|
-
auto peer_it = peers_.find(peer_id);
|
|
1197
|
-
if (peer_it != peers_.end()) {
|
|
1198
|
-
socket_t socket = peer_it->second.socket;
|
|
1199
|
-
LOG_CLIENT_INFO("Disconnecting peer " << peer_id << " due to handshake timeout");
|
|
1200
|
-
|
|
1201
|
-
remove_peer_by_id_unlocked(peer_id);
|
|
1202
|
-
poller_remove(socket);
|
|
1203
|
-
close_socket(socket);
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// =========================================================================
|
|
1209
|
-
// Connection Management
|
|
1210
|
-
// =========================================================================
|
|
1211
|
-
|
|
1212
|
-
bool RatsClient::connect_to_peer(const std::string& host, int port) {
|
|
1213
|
-
if (!running_.load()) {
|
|
1214
|
-
LOG_CLIENT_ERROR("RatsClient is not running");
|
|
1215
|
-
return false;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
LOG_CLIENT_INFO("Connecting to peer " << host << ":" << port);
|
|
1219
|
-
|
|
1220
|
-
// Check if we should ignore this address (self-connection prevention)
|
|
1221
|
-
if (should_ignore_peer(host, port)) {
|
|
1222
|
-
LOG_CLIENT_DEBUG("Ignoring connection to blocked address: " << host << ":" << port);
|
|
1223
|
-
return false;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
// Check if we're already connected
|
|
1227
|
-
std::string normalized_address = normalize_peer_address(host, port);
|
|
1228
|
-
if (is_already_connected_to_address(normalized_address)) {
|
|
1229
|
-
LOG_CLIENT_DEBUG("Already connected to " << host << ":" << port);
|
|
1230
|
-
return true; // Consider it a success since we're already connected
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
// Check peer limit
|
|
1234
|
-
if (is_peer_limit_reached()) {
|
|
1235
|
-
LOG_CLIENT_WARN("Peer limit reached, cannot connect to " << host << ":" << port);
|
|
1236
|
-
return false;
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
// Create TCP connection with timeout (blocking connect is done on a managed thread
|
|
1240
|
-
// to avoid blocking the caller, then the connected socket is handed to the IO loop).
|
|
1241
|
-
add_managed_thread(std::thread([this, host, port, normalized_address]() {
|
|
1242
|
-
socket_t client_socket = create_tcp_client(host, port, TCP_CONNECT_TIMEOUT_MS);
|
|
1243
|
-
if (!is_valid_socket(client_socket)) {
|
|
1244
|
-
LOG_CLIENT_DEBUG("Failed to connect to " << host << ":" << port);
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
LOG_CLIENT_INFO("TCP connected to " << host << ":" << port);
|
|
1249
|
-
|
|
1250
|
-
// Switch to non-blocking for the IO poller
|
|
1251
|
-
set_socket_nonblocking(client_socket);
|
|
1252
|
-
|
|
1253
|
-
std::string initial_peer_id = generate_temporary_peer_id(client_socket, normalized_address);
|
|
1254
|
-
|
|
1255
|
-
{
|
|
1256
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1257
|
-
|
|
1258
|
-
// Re-check peer limit and duplicate (could have changed while connecting)
|
|
1259
|
-
// NOTE: Use unlocked variant — peers_mutex_ is already held!
|
|
1260
|
-
if (get_peer_count_unlocked() >= max_peers_) {
|
|
1261
|
-
LOG_CLIENT_DEBUG("connect_to_peer: peer limit reached after TCP connect, aborting fd=" << client_socket);
|
|
1262
|
-
close_socket(client_socket);
|
|
1263
|
-
return;
|
|
1264
|
-
}
|
|
1265
|
-
if (address_to_peer_id_.find(normalized_address) != address_to_peer_id_.end()) {
|
|
1266
|
-
LOG_CLIENT_DEBUG("connect_to_peer: duplicate address " << normalized_address << " after TCP connect, aborting fd=" << client_socket);
|
|
1267
|
-
close_socket(client_socket);
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
RatsPeer new_peer(initial_peer_id, host, static_cast<uint16_t>(port),
|
|
1272
|
-
client_socket, normalized_address, true);
|
|
1273
|
-
new_peer.encryption_enabled = is_encryption_enabled();
|
|
1274
|
-
add_peer_unlocked(new_peer);
|
|
1275
|
-
|
|
1276
|
-
// Send initial handshake (enqueued into send buffer)
|
|
1277
|
-
auto peer_it = peers_.find(initial_peer_id);
|
|
1278
|
-
if (peer_it != peers_.end()) {
|
|
1279
|
-
if (!send_handshake_unlocked(peer_it->second, get_our_peer_id())) {
|
|
1280
|
-
LOG_CLIENT_ERROR("Failed to enqueue handshake for outgoing connection to " << host << ":" << port);
|
|
1281
|
-
remove_peer_by_id_unlocked(initial_peer_id);
|
|
1282
|
-
close_socket(client_socket);
|
|
1283
|
-
return;
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// Register with poller – PollIn for reads, PollOut to flush the queued handshake
|
|
1289
|
-
poller_add(client_socket, PollIn | PollOut);
|
|
1290
|
-
|
|
1291
|
-
LOG_CLIENT_INFO("Outgoing connection to " << host << ":" << port << " registered with IO poller");
|
|
1292
|
-
}), "connect-" + host + ":" + std::to_string(port));
|
|
1293
|
-
|
|
1294
|
-
return true;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
void RatsClient::mark_manual_disconnect(const std::string& peer_id) {
|
|
1298
|
-
std::lock_guard<std::mutex> lock(reconnect_mutex_);
|
|
1299
|
-
manual_disconnect_peers_.insert(peer_id);
|
|
1300
|
-
reconnect_queue_.erase(peer_id);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
void RatsClient::disconnect_peer(socket_t socket) {
|
|
1304
|
-
std::string peer_id = get_peer_id(socket);
|
|
1305
|
-
if (!peer_id.empty()) {
|
|
1306
|
-
mark_manual_disconnect(peer_id);
|
|
1307
|
-
}
|
|
1308
|
-
poller_remove(socket);
|
|
1309
|
-
remove_peer(socket);
|
|
1310
|
-
close_socket(socket);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
void RatsClient::disconnect_peer_by_id(const std::string& peer_id) {
|
|
1314
|
-
mark_manual_disconnect(peer_id);
|
|
1315
|
-
socket_t socket = get_peer_socket_by_id(peer_id);
|
|
1316
|
-
if (is_valid_socket(socket)) {
|
|
1317
|
-
poller_remove(socket);
|
|
1318
|
-
remove_peer(socket);
|
|
1319
|
-
close_socket(socket);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
// Peer lookup helpers (assumes peers_mutex_ is already locked)
|
|
1324
|
-
std::unordered_map<std::string, RatsPeer>::iterator RatsClient::find_peer_by_socket_unlocked(socket_t socket) {
|
|
1325
|
-
auto sock_it = socket_to_peer_id_.find(socket);
|
|
1326
|
-
if (sock_it != socket_to_peer_id_.end()) {
|
|
1327
|
-
return peers_.find(sock_it->second);
|
|
1328
|
-
}
|
|
1329
|
-
return peers_.end();
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
std::unordered_map<std::string, RatsPeer>::const_iterator RatsClient::find_peer_by_socket_unlocked(socket_t socket) const {
|
|
1333
|
-
auto sock_it = socket_to_peer_id_.find(socket);
|
|
1334
|
-
if (sock_it != socket_to_peer_id_.end()) {
|
|
1335
|
-
return peers_.find(sock_it->second);
|
|
1336
|
-
}
|
|
1337
|
-
return peers_.end();
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
// Helper methods for peer management
|
|
1341
|
-
void RatsClient::add_peer_unlocked(const RatsPeer& peer) {
|
|
1342
|
-
// Assumes peers_mutex_ is already locked
|
|
1343
|
-
peers_[peer.peer_id] = peer;
|
|
1344
|
-
socket_to_peer_id_[peer.socket] = peer.peer_id;
|
|
1345
|
-
address_to_peer_id_[peer.normalized_address] = peer.peer_id;
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
void RatsClient::remove_peer(socket_t socket) {
|
|
1349
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1350
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
1351
|
-
if (peer_it != peers_.end()) {
|
|
1352
|
-
remove_peer_by_id_unlocked(peer_it->second.peer_id);
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
void RatsClient::remove_peer_by_id_unlocked(const std::string& peer_id) {
|
|
1357
|
-
// Assumes peers_mutex_ is already locked
|
|
1358
|
-
|
|
1359
|
-
// Make a copy of peer_id to avoid use-after-free if the reference points to memory that gets freed
|
|
1360
|
-
std::string peer_id_copy = peer_id;
|
|
1361
|
-
|
|
1362
|
-
auto it = peers_.find(peer_id_copy);
|
|
1363
|
-
if (it != peers_.end()) {
|
|
1364
|
-
// Decrement validated peer count if this peer had completed handshake
|
|
1365
|
-
if (it->second.is_handshake_completed()) {
|
|
1366
|
-
validated_peer_count_.fetch_sub(1, std::memory_order_relaxed);
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
// Copy the values we need before erasing to avoid use-after-free
|
|
1370
|
-
socket_t peer_socket = it->second.socket;
|
|
1371
|
-
std::string peer_normalized_address = it->second.normalized_address;
|
|
1372
|
-
|
|
1373
|
-
socket_to_peer_id_.erase(peer_socket);
|
|
1374
|
-
address_to_peer_id_.erase(peer_normalized_address);
|
|
1375
|
-
peers_.erase(it);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
bool RatsClient::is_already_connected_to_address(const std::string& normalized_address) const {
|
|
1380
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1381
|
-
return address_to_peer_id_.find(normalized_address) != address_to_peer_id_.end();
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
void RatsClient::add_ignored_address(const std::string& ip_address) {
|
|
1385
|
-
std::lock_guard<std::mutex> lock(local_addresses_mutex_);
|
|
1386
|
-
|
|
1387
|
-
auto [it, inserted] = local_interface_addresses_.insert(ip_address);
|
|
1388
|
-
if (inserted) {
|
|
1389
|
-
LOG_CLIENT_INFO("Added " << ip_address << " to ignore list");
|
|
1390
|
-
} else {
|
|
1391
|
-
LOG_CLIENT_DEBUG("IP address " << ip_address << " already in ignore list");
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
//common localhost addresses
|
|
1396
|
-
static constexpr std::array<std::string_view,5> localhost_addrs{"127.0.0.1", "::1", "0.0.0.0", "::", "localhost"};
|
|
1397
|
-
|
|
1398
|
-
// Local interface address blocking methods
|
|
1399
|
-
void RatsClient::initialize_local_addresses() {
|
|
1400
|
-
std::lock_guard<std::mutex> lock(local_addresses_mutex_);
|
|
1401
|
-
|
|
1402
|
-
// (Re)enumerate the host's interface addresses. This is also called on every
|
|
1403
|
-
// network change, so it must be a diff, not a blind insert: drop auto-detected
|
|
1404
|
-
// addresses that have disappeared (a removed IP must stop being treated as
|
|
1405
|
-
// "ourselves"), while preserving localhost entries and any externally-
|
|
1406
|
-
// discovered / manually-ignored addresses (STUN reflexive, mapped external IP,
|
|
1407
|
-
// user add_ignored_address()), which live in the same set.
|
|
1408
|
-
auto addrs = network_utils::get_local_interface_addresses();
|
|
1409
|
-
std::unordered_set<std::string> new_auto(addrs.begin(), addrs.end());
|
|
1410
|
-
|
|
1411
|
-
for (const auto& old : auto_interface_addresses_) {
|
|
1412
|
-
if (new_auto.find(old) == new_auto.end()) {
|
|
1413
|
-
local_interface_addresses_.erase(old);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
for (const auto& addr : addrs) {
|
|
1417
|
-
local_interface_addresses_.insert(addr);
|
|
1418
|
-
}
|
|
1419
|
-
for (const auto& addr : localhost_addrs) {
|
|
1420
|
-
local_interface_addresses_.emplace(std::string(addr));
|
|
1421
|
-
}
|
|
1422
|
-
auto_interface_addresses_ = std::move(new_auto);
|
|
1423
|
-
|
|
1424
|
-
LOG_CLIENT_INFO("Local interface addresses: " << local_interface_addresses_.size()
|
|
1425
|
-
<< " blocked (" << addrs.size() << " from interfaces)");
|
|
1426
|
-
for (const auto& addr : local_interface_addresses_) {
|
|
1427
|
-
LOG_CLIENT_DEBUG(" - " << addr);
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
bool RatsClient::is_blocked_address(const std::string& ip_address) const {
|
|
1432
|
-
std::lock_guard<std::mutex> lock(local_addresses_mutex_);
|
|
1433
|
-
return local_interface_addresses_.count(ip_address) > 0;
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
bool RatsClient::can_connect_to_peer(const std::string& ip, int port) const {
|
|
1437
|
-
if (should_ignore_peer(ip, port)) {
|
|
1438
|
-
LOG_CLIENT_DEBUG("Ignoring peer " << ip << ":" << port << " - blocked address");
|
|
1439
|
-
return false;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
std::string normalized_address = normalize_peer_address(ip, port);
|
|
1443
|
-
if (is_already_connected_to_address(normalized_address)) {
|
|
1444
|
-
LOG_CLIENT_DEBUG("Already connected to " << normalized_address);
|
|
1445
|
-
return false;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
if (is_peer_limit_reached()) {
|
|
1449
|
-
LOG_CLIENT_DEBUG("Peer limit reached, cannot connect to " << ip << ":" << port);
|
|
1450
|
-
return false;
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
return true;
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
bool RatsClient::should_ignore_peer(const std::string& ip, int port) const {
|
|
1457
|
-
// Check if this is a well-known localhost address
|
|
1458
|
-
bool is_localhost = std::find(localhost_addrs.begin(), localhost_addrs.end(), ip) != localhost_addrs.end();
|
|
1459
|
-
|
|
1460
|
-
if (is_localhost) {
|
|
1461
|
-
// Block self-connections (same port on localhost)
|
|
1462
|
-
if (port == listen_port_) {
|
|
1463
|
-
LOG_CLIENT_DEBUG("Ignoring peer " << ip << ":" << port << " - localhost with same port");
|
|
1464
|
-
return true;
|
|
1465
|
-
}
|
|
1466
|
-
// Allow localhost on different ports (for testing)
|
|
1467
|
-
LOG_CLIENT_DEBUG("Allowing localhost peer " << ip << ":" << port << " on different port");
|
|
1468
|
-
return false;
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
// Block non-localhost local interface addresses
|
|
1472
|
-
if (is_blocked_address(ip)) {
|
|
1473
|
-
LOG_CLIENT_DEBUG("Ignoring peer " << ip << ":" << port << " - matches local interface address");
|
|
1474
|
-
return true;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
return false;
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
// =========================================================================
|
|
1481
|
-
// Data Transmission Methods
|
|
1482
|
-
// =========================================================================
|
|
1483
|
-
|
|
1484
|
-
// Helper method to create a message with header
|
|
1485
|
-
std::vector<uint8_t> RatsClient::create_message_with_header(const std::vector<uint8_t>& payload, MessageDataType type) {
|
|
1486
|
-
MessageHeader header(type);
|
|
1487
|
-
std::vector<uint8_t> header_bytes = header.serialize();
|
|
1488
|
-
|
|
1489
|
-
// Combine header + payload
|
|
1490
|
-
std::vector<uint8_t> message;
|
|
1491
|
-
message.reserve(header_bytes.size() + payload.size());
|
|
1492
|
-
message.insert(message.end(), header_bytes.begin(), header_bytes.end());
|
|
1493
|
-
message.insert(message.end(), payload.begin(), payload.end());
|
|
1494
|
-
|
|
1495
|
-
return message;
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
// Helper method to parse message header and extract payload
|
|
1499
|
-
bool RatsClient::parse_message_with_header(const std::vector<uint8_t>& message, MessageHeader& header, std::vector<uint8_t>& payload) const {
|
|
1500
|
-
// Check if message is large enough to contain header
|
|
1501
|
-
if (message.size() < MessageHeader::HEADER_SIZE) {
|
|
1502
|
-
LOG_CLIENT_DEBUG("Message too small to contain header: " << message.size() << " bytes");
|
|
1503
|
-
return false;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
// Extract header bytes
|
|
1507
|
-
std::vector<uint8_t> header_bytes(message.begin(), message.begin() + MessageHeader::HEADER_SIZE);
|
|
1508
|
-
|
|
1509
|
-
// Parse header
|
|
1510
|
-
if (!MessageHeader::deserialize(header_bytes, header)) {
|
|
1511
|
-
LOG_CLIENT_DEBUG("Failed to parse message header - invalid magic number or format");
|
|
1512
|
-
return false;
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
// Validate header
|
|
1516
|
-
if (!header.is_valid_type()) {
|
|
1517
|
-
LOG_CLIENT_WARN("Invalid message data type: " << static_cast<int>(header.type));
|
|
1518
|
-
return false;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
// Extract payload
|
|
1522
|
-
payload.assign(message.begin() + MessageHeader::HEADER_SIZE, message.end());
|
|
1523
|
-
|
|
1524
|
-
return true;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
// Async send – enqueues header + (optionally encrypted) payload into the peer's
|
|
1528
|
-
// ChainedSendBuffer. Does NOT require peers_mutex_; caller passes cached peer data.
|
|
1529
|
-
// The shared_ptr keeps the cipher alive even if the peer is removed concurrently.
|
|
1530
|
-
bool RatsClient::send_binary_to_peer_unlocked(socket_t socket, const std::vector<uint8_t>& data,
|
|
1531
|
-
MessageDataType message_type,
|
|
1532
|
-
std::shared_ptr<rats::NoiseCipherState> send_cipher,
|
|
1533
|
-
const std::string& peer_id_for_logging) {
|
|
1534
|
-
if (!running_.load()) {
|
|
1535
|
-
return false;
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// Create message with specified header type
|
|
1539
|
-
std::vector<uint8_t> message_with_header = create_message_with_header(data, message_type);
|
|
1540
|
-
|
|
1541
|
-
if (send_cipher) {
|
|
1542
|
-
// Encrypt the message before enqueuing
|
|
1543
|
-
std::vector<uint8_t> ciphertext(message_with_header.size() + rats::NOISE_TAG_SIZE);
|
|
1544
|
-
size_t ct_len = send_cipher->encrypt_with_ad(
|
|
1545
|
-
nullptr, 0,
|
|
1546
|
-
message_with_header.data(), message_with_header.size(),
|
|
1547
|
-
ciphertext.data()
|
|
1548
|
-
);
|
|
1549
|
-
|
|
1550
|
-
if (ct_len == 0) {
|
|
1551
|
-
LOG_CLIENT_ERROR("Failed to encrypt message for peer: " << peer_id_for_logging);
|
|
1552
|
-
return false;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
ciphertext.resize(ct_len);
|
|
1556
|
-
LOG_CLIENT_DEBUG("Enqueuing encrypted message for " << peer_id_for_logging << " (" << ct_len << " bytes)");
|
|
1557
|
-
|
|
1558
|
-
return enqueue_message(socket, ciphertext);
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
// Unencrypted path
|
|
1562
|
-
return enqueue_message(socket, message_with_header);
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
bool RatsClient::send_binary_to_peer(socket_t socket, const std::vector<uint8_t>& data, MessageDataType message_type) {
|
|
1566
|
-
if (!running_.load()) {
|
|
1567
|
-
return false;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
// Cache peer data under lock, then release lock before sending
|
|
1571
|
-
std::string peer_id;
|
|
1572
|
-
std::shared_ptr<rats::NoiseCipherState> send_cipher;
|
|
1573
|
-
|
|
1574
|
-
{
|
|
1575
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1576
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
1577
|
-
if (peer_it != peers_.end()) {
|
|
1578
|
-
peer_id = peer_it->second.peer_id;
|
|
1579
|
-
if (peer_it->second.is_noise_encrypted()) {
|
|
1580
|
-
send_cipher = peer_it->second.send_cipher; // shared_ptr copy keeps cipher alive
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
// peers_mutex_ released -- safe to do potentially slow TCP send
|
|
1586
|
-
return send_binary_to_peer_unlocked(socket, data, message_type, send_cipher, peer_id);
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
bool RatsClient::send_string_to_peer(socket_t socket, const std::string& data) {
|
|
1590
|
-
// Convert string to binary and use the primary send_binary_to_peer method
|
|
1591
|
-
std::vector<uint8_t> binary_data(data.begin(), data.end());
|
|
1592
|
-
return send_binary_to_peer(socket, binary_data, MessageDataType::STRING);
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
std::vector<uint8_t> RatsClient::json_to_binary(const nlohmann::json& data) {
|
|
1596
|
-
std::string s = data.dump();
|
|
1597
|
-
return {s.begin(), s.end()};
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
bool RatsClient::send_json_to_peer(socket_t socket, const nlohmann::json& data) {
|
|
1601
|
-
try {
|
|
1602
|
-
return send_binary_to_peer(socket, json_to_binary(data), MessageDataType::JSON);
|
|
1603
|
-
} catch (const nlohmann::json::exception& e) {
|
|
1604
|
-
LOG_CLIENT_ERROR("Failed to serialize JSON message: " << e.what());
|
|
1605
|
-
return false;
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
bool RatsClient::send_binary_to_peer_id(const std::string& peer_id, const std::vector<uint8_t>& data, MessageDataType message_type) {
|
|
1610
|
-
// Cache peer data under lock, then release before sending
|
|
1611
|
-
socket_t socket;
|
|
1612
|
-
std::shared_ptr<rats::NoiseCipherState> send_cipher;
|
|
1613
|
-
|
|
1614
|
-
{
|
|
1615
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1616
|
-
auto it = peers_.find(peer_id);
|
|
1617
|
-
if (it == peers_.end() || !it->second.is_handshake_completed()) {
|
|
1618
|
-
return false;
|
|
1619
|
-
}
|
|
1620
|
-
socket = it->second.socket;
|
|
1621
|
-
if (it->second.is_noise_encrypted()) {
|
|
1622
|
-
send_cipher = it->second.send_cipher; // shared_ptr copy keeps cipher alive
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
// peers_mutex_ released -- safe to do potentially slow TCP send
|
|
1627
|
-
return send_binary_to_peer_unlocked(socket, data, message_type, send_cipher, peer_id);
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
bool RatsClient::send_string_to_peer_id(const std::string& peer_id, const std::string& data) {
|
|
1631
|
-
// Convert string to binary and use primary binary method with STRING type
|
|
1632
|
-
std::vector<uint8_t> binary_data(data.begin(), data.end());
|
|
1633
|
-
return send_binary_to_peer_id(peer_id, binary_data, MessageDataType::STRING);
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
bool RatsClient::send_json_to_peer_id(const std::string& peer_id, const nlohmann::json& data) {
|
|
1637
|
-
try {
|
|
1638
|
-
return send_binary_to_peer_id(peer_id, json_to_binary(data), MessageDataType::JSON);
|
|
1639
|
-
} catch (const nlohmann::json::exception& e) {
|
|
1640
|
-
LOG_CLIENT_ERROR("Failed to serialize JSON message: " << e.what());
|
|
1641
|
-
return false;
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
int RatsClient::broadcast_json_to_peers(const nlohmann::json& data) {
|
|
1646
|
-
try {
|
|
1647
|
-
return broadcast_binary_to_peers(json_to_binary(data), MessageDataType::JSON);
|
|
1648
|
-
} catch (const nlohmann::json::exception& e) {
|
|
1649
|
-
LOG_CLIENT_ERROR("Failed to serialize JSON message for broadcast: " << e.what());
|
|
1650
|
-
return 0;
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
int RatsClient::broadcast_binary_to_peers(const std::vector<uint8_t>& data, MessageDataType message_type) {
|
|
1655
|
-
if (!running_.load()) {
|
|
1656
|
-
return 0;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
// Collect targets under lock, then enqueue outside
|
|
1660
|
-
std::vector<PeerSendTarget> targets;
|
|
1661
|
-
{
|
|
1662
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1663
|
-
targets.reserve(peers_.size());
|
|
1664
|
-
for (const auto& [id, peer] : peers_) {
|
|
1665
|
-
if (peer.is_handshake_completed()) {
|
|
1666
|
-
targets.push_back({peer.socket, peer.peer_id,
|
|
1667
|
-
peer.is_noise_encrypted() ? peer.send_cipher : nullptr});
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
int sent_count = 0;
|
|
1673
|
-
for (const auto& t : targets) {
|
|
1674
|
-
if (send_binary_to_peer_unlocked(t.socket, data, message_type, t.send_cipher, t.peer_id)) {
|
|
1675
|
-
sent_count++;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
return sent_count;
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
int RatsClient::broadcast_string_to_peers(const std::string& data) {
|
|
1682
|
-
// Convert string to binary and use primary binary method with STRING type
|
|
1683
|
-
std::vector<uint8_t> binary_data(data.begin(), data.end());
|
|
1684
|
-
return broadcast_binary_to_peers(binary_data, MessageDataType::STRING);
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
// =========================================================================
|
|
1688
|
-
// Peer Information and Management
|
|
1689
|
-
// =========================================================================
|
|
1690
|
-
|
|
1691
|
-
std::string RatsClient::get_our_peer_id() const {
|
|
1692
|
-
return our_peer_id_;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
int RatsClient::get_peer_count_unlocked() const {
|
|
1696
|
-
// Returns the cached validated peer count (O(1) instead of O(N) scan)
|
|
1697
|
-
return validated_peer_count_.load(std::memory_order_relaxed);
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
int RatsClient::get_peer_count() const {
|
|
1701
|
-
return validated_peer_count_.load(std::memory_order_relaxed);
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
std::string RatsClient::get_peer_id(socket_t socket) const {
|
|
1705
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1706
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
1707
|
-
return (peer_it != peers_.end()) ? peer_it->second.peer_id : "";
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
socket_t RatsClient::get_peer_socket_by_id(const std::string& peer_id) const {
|
|
1711
|
-
// Atomic operation - lock once and return copy to avoid race condition
|
|
1712
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1713
|
-
auto it = peers_.find(peer_id);
|
|
1714
|
-
if (it != peers_.end()) {
|
|
1715
|
-
return it->second.socket;
|
|
1716
|
-
}
|
|
1717
|
-
return INVALID_SOCKET_VALUE;
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
std::vector<RatsPeer> RatsClient::get_all_peers() const {
|
|
1721
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1722
|
-
std::vector<RatsPeer> result;
|
|
1723
|
-
result.reserve(peers_.size());
|
|
1724
|
-
|
|
1725
|
-
for (const auto& pair : peers_) {
|
|
1726
|
-
result.push_back(pair.second);
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
return result;
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
std::vector<RatsPeer> RatsClient::get_validated_peers() const {
|
|
1733
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1734
|
-
std::vector<RatsPeer> result;
|
|
1735
|
-
|
|
1736
|
-
for (const auto& pair : peers_) {
|
|
1737
|
-
if (pair.second.is_handshake_completed()) {
|
|
1738
|
-
result.push_back(pair.second);
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
return result;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
std::vector<RatsPeer> RatsClient::get_random_peers(int max_count, const std::string& exclude_peer_id) const {
|
|
1746
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1747
|
-
|
|
1748
|
-
std::vector<RatsPeer> all_validated_peers;
|
|
1749
|
-
|
|
1750
|
-
// Get all validated peers excluding the specified peer
|
|
1751
|
-
for (const auto& pair : peers_) {
|
|
1752
|
-
const RatsPeer& peer = pair.second;
|
|
1753
|
-
if (peer.is_handshake_completed() && peer.peer_id != exclude_peer_id) {
|
|
1754
|
-
all_validated_peers.push_back(peer);
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
// If we have fewer peers than requested, return all
|
|
1759
|
-
if (all_validated_peers.size() <= static_cast<size_t>(max_count)) {
|
|
1760
|
-
return all_validated_peers;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
// Randomly select peers
|
|
1764
|
-
std::vector<RatsPeer> selected_peers;
|
|
1765
|
-
std::random_device rd;
|
|
1766
|
-
std::mt19937 gen(rd());
|
|
1767
|
-
|
|
1768
|
-
// Use random sampling to select peers
|
|
1769
|
-
std::sample(all_validated_peers.begin(), all_validated_peers.end(),
|
|
1770
|
-
std::back_inserter(selected_peers), max_count, gen);
|
|
1771
|
-
|
|
1772
|
-
return selected_peers;
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
std::optional<RatsPeer> RatsClient::get_peer_by_id(const std::string& peer_id) const {
|
|
1776
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1777
|
-
auto it = peers_.find(peer_id);
|
|
1778
|
-
if (it != peers_.end()) {
|
|
1779
|
-
return it->second;
|
|
1780
|
-
}
|
|
1781
|
-
return std::nullopt;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
std::optional<RatsPeer> RatsClient::get_peer_by_socket(socket_t socket) const {
|
|
1785
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1786
|
-
auto peer_it = find_peer_by_socket_unlocked(socket);
|
|
1787
|
-
if (peer_it != peers_.end()) {
|
|
1788
|
-
return peer_it->second;
|
|
1789
|
-
}
|
|
1790
|
-
return std::nullopt;
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
// Peer limit management methods
|
|
1794
|
-
int RatsClient::get_max_peers() const {
|
|
1795
|
-
return max_peers_;
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
void RatsClient::set_max_peers(int max_peers) {
|
|
1799
|
-
max_peers_ = max_peers;
|
|
1800
|
-
LOG_CLIENT_INFO("Maximum peers set to " << max_peers_);
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
bool RatsClient::is_peer_limit_reached() const {
|
|
1804
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1805
|
-
return get_peer_count_unlocked() >= max_peers_;
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
std::string RatsClient::generate_temporary_peer_id(socket_t socket, const std::string& connection_info) {
|
|
1809
|
-
// Generate unique hash ID using timestamp, socket, connection info, and random component
|
|
1810
|
-
auto now = std::chrono::high_resolution_clock::now();
|
|
1811
|
-
auto timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
|
|
1812
|
-
|
|
1813
|
-
// Create a random component
|
|
1814
|
-
std::random_device rd;
|
|
1815
|
-
std::mt19937 gen(rd());
|
|
1816
|
-
std::uniform_int_distribution<> dis(0, 255);
|
|
1817
|
-
|
|
1818
|
-
// Build hash string
|
|
1819
|
-
std::ostringstream hash_stream;
|
|
1820
|
-
hash_stream << std::hex << timestamp << "_" << socket << "_";
|
|
1821
|
-
|
|
1822
|
-
// Add connection info hash
|
|
1823
|
-
std::hash<std::string> hasher;
|
|
1824
|
-
hash_stream << hasher(connection_info) << "_";
|
|
1825
|
-
|
|
1826
|
-
// Add random component
|
|
1827
|
-
for (int i = 0; i < 8; ++i) {
|
|
1828
|
-
hash_stream << std::setfill('0') << std::setw(2) << dis(gen);
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
return hash_stream.str();
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
std::string RatsClient::normalize_peer_address(const std::string& ip, int port) const {
|
|
1835
|
-
// Normalize IPv6 addresses and create consistent format
|
|
1836
|
-
std::string normalized_ip = ip;
|
|
1837
|
-
|
|
1838
|
-
// Remove brackets from IPv6 addresses if present
|
|
1839
|
-
if (!normalized_ip.empty() && normalized_ip.front() == '[' && normalized_ip.back() == ']') {
|
|
1840
|
-
normalized_ip = normalized_ip.substr(1, normalized_ip.length() - 2);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
// Handle localhost variations
|
|
1844
|
-
if (normalized_ip == "localhost" || normalized_ip == "::1") {
|
|
1845
|
-
normalized_ip = "127.0.0.1";
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
// For IPv6 addresses, add brackets for consistency
|
|
1849
|
-
if (normalized_ip.find(':') != std::string::npos && normalized_ip.find('.') == std::string::npos) {
|
|
1850
|
-
// This is likely an IPv6 address (contains colons but no dots)
|
|
1851
|
-
return "[" + normalized_ip + "]:" + std::to_string(port);
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
return normalized_ip + ":" + std::to_string(port);
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
// =========================================================================
|
|
1858
|
-
// Callback Registration
|
|
1859
|
-
// =========================================================================
|
|
1860
|
-
|
|
1861
|
-
void RatsClient::set_connection_callback(ConnectionCallback callback) {
|
|
1862
|
-
connection_callback_ = callback;
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
void RatsClient::set_binary_data_callback(BinaryDataCallback callback) {
|
|
1866
|
-
binary_data_callback_ = callback;
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1869
|
-
void RatsClient::set_string_data_callback(StringDataCallback callback) {
|
|
1870
|
-
string_data_callback_ = callback;
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
void RatsClient::set_json_data_callback(JsonDataCallback callback) {
|
|
1874
|
-
json_data_callback_ = callback;
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
void RatsClient::set_disconnect_callback(DisconnectCallback callback) {
|
|
1878
|
-
disconnect_callback_ = callback;
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
// =========================================================================
|
|
1882
|
-
// Protocol Configuration
|
|
1883
|
-
// =========================================================================
|
|
1884
|
-
|
|
1885
|
-
void RatsClient::set_protocol_name(const std::string& protocol_name) {
|
|
1886
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
1887
|
-
custom_protocol_name_ = protocol_name;
|
|
1888
|
-
LOG_CLIENT_INFO("Protocol name set to: " << protocol_name);
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
void RatsClient::set_protocol_version(const std::string& protocol_version) {
|
|
1892
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
1893
|
-
custom_protocol_version_ = protocol_version;
|
|
1894
|
-
LOG_CLIENT_INFO("Protocol version set to: " << protocol_version);
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
std::string RatsClient::get_protocol_name() const {
|
|
1898
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
1899
|
-
return custom_protocol_name_;
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
std::string RatsClient::get_protocol_version() const {
|
|
1903
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
1904
|
-
return custom_protocol_version_;
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
// =========================================================================
|
|
1908
|
-
// Message Exchange API
|
|
1909
|
-
// =========================================================================
|
|
1910
|
-
|
|
1911
|
-
void RatsClient::on(const std::string& message_type, MessageCallback callback) {
|
|
1912
|
-
std::lock_guard<std::mutex> lock(message_handlers_mutex_);
|
|
1913
|
-
message_handlers_[message_type].emplace_back(callback, false); // false = not once
|
|
1914
|
-
LOG_CLIENT_DEBUG("Registered handler for message type: " << message_type);
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
void RatsClient::once(const std::string& message_type, MessageCallback callback) {
|
|
1918
|
-
std::lock_guard<std::mutex> lock(message_handlers_mutex_);
|
|
1919
|
-
message_handlers_[message_type].emplace_back(callback, true); // true = once
|
|
1920
|
-
LOG_CLIENT_DEBUG("Registered one-time handler for message type: " << message_type);
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
void RatsClient::off(const std::string& message_type) {
|
|
1924
|
-
std::lock_guard<std::mutex> lock(message_handlers_mutex_);
|
|
1925
|
-
auto it = message_handlers_.find(message_type);
|
|
1926
|
-
if (it != message_handlers_.end()) {
|
|
1927
|
-
size_t removed_count = it->second.size();
|
|
1928
|
-
message_handlers_.erase(it);
|
|
1929
|
-
LOG_CLIENT_DEBUG("Removed " << removed_count << " handlers for message type: " << message_type);
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
void RatsClient::send(const std::string& message_type, const nlohmann::json& data, SendCallback callback) {
|
|
1934
|
-
if (!running_.load()) {
|
|
1935
|
-
LOG_CLIENT_ERROR("Cannot send message '" << message_type << "' - client is not running");
|
|
1936
|
-
if (callback) {
|
|
1937
|
-
callback(false, "Client is not running");
|
|
1938
|
-
}
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
LOG_CLIENT_DEBUG("Sending broadcast message type '" << message_type << "'");
|
|
1943
|
-
|
|
1944
|
-
// Create rats message
|
|
1945
|
-
nlohmann::json message = create_rats_message(message_type, data, get_our_peer_id());
|
|
1946
|
-
|
|
1947
|
-
// Broadcast to all validated peers
|
|
1948
|
-
int sent_count = broadcast_rats_message(message);
|
|
1949
|
-
|
|
1950
|
-
LOG_CLIENT_DEBUG("Broadcasted message type '" << message_type << "' to " << sent_count << " peers");
|
|
1951
|
-
|
|
1952
|
-
if (callback) {
|
|
1953
|
-
if (sent_count > 0) {
|
|
1954
|
-
callback(true, "");
|
|
1955
|
-
} else {
|
|
1956
|
-
LOG_CLIENT_WARN("No peers to send message to");
|
|
1957
|
-
callback(false, "No peers to send message to");
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
void RatsClient::send(const std::string& peer_id, const std::string& message_type, const nlohmann::json& data, SendCallback callback) {
|
|
1963
|
-
if (!running_.load()) {
|
|
1964
|
-
LOG_CLIENT_ERROR("Cannot send message '" << message_type << "' to peer " << peer_id << " - client is not running");
|
|
1965
|
-
if (callback) {
|
|
1966
|
-
callback(false, "Client is not running");
|
|
1967
|
-
}
|
|
1968
|
-
return;
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
LOG_CLIENT_DEBUG("Sending targeted message type '" << message_type << "' to peer " << peer_id);
|
|
1972
|
-
|
|
1973
|
-
// Create rats message
|
|
1974
|
-
nlohmann::json message = create_rats_message(message_type, data, get_our_peer_id());
|
|
1975
|
-
|
|
1976
|
-
// Send to specific peer
|
|
1977
|
-
socket_t target_socket = INVALID_SOCKET_VALUE;
|
|
1978
|
-
bool peer_found = false;
|
|
1979
|
-
bool handshake_completed = false;
|
|
1980
|
-
|
|
1981
|
-
{
|
|
1982
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
1983
|
-
auto it = peers_.find(peer_id);
|
|
1984
|
-
if (it != peers_.end()) {
|
|
1985
|
-
peer_found = true;
|
|
1986
|
-
handshake_completed = it->second.is_handshake_completed();
|
|
1987
|
-
if (handshake_completed) {
|
|
1988
|
-
target_socket = it->second.socket;
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
if (!peer_found) {
|
|
1994
|
-
LOG_CLIENT_ERROR("Cannot send message '" << message_type << "' - peer not found: " << peer_id);
|
|
1995
|
-
if (callback) {
|
|
1996
|
-
callback(false, "Peer not found: " + peer_id);
|
|
1997
|
-
}
|
|
1998
|
-
return;
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
if (!handshake_completed) {
|
|
2002
|
-
LOG_CLIENT_ERROR("Cannot send message '" << message_type << "' - peer handshake not completed: " << peer_id);
|
|
2003
|
-
if (callback) {
|
|
2004
|
-
callback(false, "Peer handshake not completed: " + peer_id);
|
|
2005
|
-
}
|
|
2006
|
-
return;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
bool success = send_json_to_peer(target_socket, message);
|
|
2010
|
-
|
|
2011
|
-
LOG_CLIENT_DEBUG("Sent message type '" << message_type << "' to peer " << peer_id << " - " << (success ? "success" : "failed"));
|
|
2012
|
-
|
|
2013
|
-
if (callback) {
|
|
2014
|
-
if (success) {
|
|
2015
|
-
callback(true, "");
|
|
2016
|
-
} else {
|
|
2017
|
-
callback(false, "Failed to send message to peer: " + peer_id);
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
|
|
2022
|
-
// Message exchange system helpers
|
|
2023
|
-
void RatsClient::call_message_handlers(const std::string& message_type, const std::string& peer_id, const nlohmann::json& data) {
|
|
2024
|
-
std::vector<MessageHandler> handlers_to_call;
|
|
2025
|
-
|
|
2026
|
-
// Get handlers to call and remove once handlers atomically
|
|
2027
|
-
{
|
|
2028
|
-
std::lock_guard<std::mutex> lock(message_handlers_mutex_);
|
|
2029
|
-
auto it = message_handlers_.find(message_type);
|
|
2030
|
-
if (it == message_handlers_.end()) {
|
|
2031
|
-
LOG_CLIENT_DEBUG("No handlers registered for message type '" << message_type << "'");
|
|
2032
|
-
return;
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
handlers_to_call = it->second;
|
|
2036
|
-
|
|
2037
|
-
// Remove once handlers using erase-remove idiom
|
|
2038
|
-
it->second.erase(
|
|
2039
|
-
std::remove_if(it->second.begin(), it->second.end(),
|
|
2040
|
-
[](const MessageHandler& h) { return h.is_once; }),
|
|
2041
|
-
it->second.end());
|
|
2042
|
-
}
|
|
2043
|
-
|
|
2044
|
-
LOG_CLIENT_DEBUG("Calling " << handlers_to_call.size() << " handlers for message type '" << message_type << "'");
|
|
2045
|
-
|
|
2046
|
-
// Call handlers outside of mutex to avoid deadlock
|
|
2047
|
-
for (const auto& handler : handlers_to_call) {
|
|
2048
|
-
try {
|
|
2049
|
-
handler.callback(peer_id, data);
|
|
2050
|
-
} catch (const std::exception& e) {
|
|
2051
|
-
LOG_CLIENT_ERROR("Exception in message handler for type '" << message_type << "': " << e.what());
|
|
2052
|
-
} catch (...) {
|
|
2053
|
-
LOG_CLIENT_ERROR("Unknown exception in message handler for type '" << message_type << "'");
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
// =========================================================================
|
|
2059
|
-
// Rats Protocol Message Handling
|
|
2060
|
-
// =========================================================================
|
|
2061
|
-
|
|
2062
|
-
nlohmann::json RatsClient::create_rats_message(const std::string& type, const nlohmann::json& payload, const std::string& sender_peer_id) {
|
|
2063
|
-
nlohmann::json message;
|
|
2064
|
-
message["rats_protocol"] = true;
|
|
2065
|
-
message["type"] = type;
|
|
2066
|
-
message["payload"] = payload;
|
|
2067
|
-
message["sender_peer_id"] = sender_peer_id;
|
|
2068
|
-
message["timestamp"] = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
2069
|
-
std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
|
2070
|
-
|
|
2071
|
-
return message;
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
void RatsClient::handle_rats_message(socket_t socket, const std::string& peer_id, const nlohmann::json& message) {
|
|
2075
|
-
try {
|
|
2076
|
-
std::string message_type = message.value("type", "");
|
|
2077
|
-
nlohmann::json payload = message.value("payload", nlohmann::json::object());
|
|
2078
|
-
std::string sender_peer_id = message.value("sender_peer_id", "");
|
|
2079
|
-
|
|
2080
|
-
LOG_CLIENT_DEBUG("Received rats message type '" << message_type << "' from " << peer_id);
|
|
2081
|
-
|
|
2082
|
-
// Call registered message handlers for all message types (including custom ones)
|
|
2083
|
-
call_message_handlers(message_type, sender_peer_id.empty() ? peer_id : sender_peer_id, payload);
|
|
2084
|
-
|
|
2085
|
-
// Handle built-in message types for internal functionality
|
|
2086
|
-
if (message_type == "peer") {
|
|
2087
|
-
handle_peer_exchange_message(socket, peer_id, payload);
|
|
2088
|
-
}
|
|
2089
|
-
else if (message_type == "peers_request") {
|
|
2090
|
-
handle_peers_request_message(socket, peer_id, payload);
|
|
2091
|
-
}
|
|
2092
|
-
else if (message_type == "peers_response") {
|
|
2093
|
-
handle_peers_response_message(socket, peer_id, payload);
|
|
2094
|
-
}
|
|
2095
|
-
// Custom message types are now handled by registered handlers above
|
|
2096
|
-
// No need for else clause - all message types are valid if they have registered handlers
|
|
2097
|
-
|
|
2098
|
-
} catch (const nlohmann::json::exception& e) {
|
|
2099
|
-
LOG_CLIENT_ERROR("Failed to handle rats message: " << e.what());
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
|
-
void RatsClient::handle_peer_exchange_message(socket_t socket, const std::string& peer_id, const nlohmann::json& payload) {
|
|
2104
|
-
try {
|
|
2105
|
-
std::string exchanged_ip = payload.value("ip", "");
|
|
2106
|
-
int exchanged_port = payload.value("port", 0);
|
|
2107
|
-
std::string exchanged_peer_id = payload.value("peer_id", "");
|
|
2108
|
-
|
|
2109
|
-
if (exchanged_ip.empty() || exchanged_port <= 0 || exchanged_peer_id.empty()) {
|
|
2110
|
-
LOG_CLIENT_WARN("Invalid peer exchange message from " << peer_id);
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
|
|
2114
|
-
LOG_CLIENT_INFO("Received peer exchange: " << exchanged_ip << ":" << exchanged_port << " (peer_id: " << exchanged_peer_id << ")");
|
|
2115
|
-
|
|
2116
|
-
if (!can_connect_to_peer(exchanged_ip, exchanged_port)) {
|
|
2117
|
-
return;
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
// Try to connect to the exchanged peer (non-blocking)
|
|
2121
|
-
add_managed_thread(std::thread([this, exchanged_ip, exchanged_port, exchanged_peer_id]() {
|
|
2122
|
-
if (connect_to_peer(exchanged_ip, exchanged_port)) {
|
|
2123
|
-
LOG_CLIENT_INFO("Successfully connected to exchanged peer: " << exchanged_ip << ":" << exchanged_port);
|
|
2124
|
-
} else {
|
|
2125
|
-
LOG_CLIENT_DEBUG("Failed to connect to exchanged peer: " << exchanged_ip << ":" << exchanged_port);
|
|
2126
|
-
}
|
|
2127
|
-
}), "peer-exchange-connect-" + exchanged_peer_id.substr(0, 8));
|
|
2128
|
-
|
|
2129
|
-
} catch (const nlohmann::json::exception& e) {
|
|
2130
|
-
LOG_CLIENT_ERROR("Failed to handle peer exchange message: " << e.what());
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
|
-
// General broadcasting function
|
|
2135
|
-
int RatsClient::broadcast_rats_message(const nlohmann::json& message, const std::string& exclude_peer_id, bool validated_only) {
|
|
2136
|
-
// Serialize JSON once before iterating
|
|
2137
|
-
std::string json_string;
|
|
2138
|
-
try {
|
|
2139
|
-
json_string = message.dump();
|
|
2140
|
-
} catch (const nlohmann::json::exception& e) {
|
|
2141
|
-
LOG_CLIENT_ERROR("Failed to serialize JSON message for broadcast: " << e.what());
|
|
2142
|
-
return 0;
|
|
2143
|
-
}
|
|
2144
|
-
std::vector<uint8_t> binary_data(json_string.begin(), json_string.end());
|
|
2145
|
-
|
|
2146
|
-
// Collect targets under lock, then enqueue outside
|
|
2147
|
-
std::vector<PeerSendTarget> targets;
|
|
2148
|
-
{
|
|
2149
|
-
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
2150
|
-
targets.reserve(peers_.size());
|
|
2151
|
-
for (const auto& [id, peer] : peers_) {
|
|
2152
|
-
if (!exclude_peer_id.empty() && peer.peer_id == exclude_peer_id) {
|
|
2153
|
-
continue;
|
|
2154
|
-
}
|
|
2155
|
-
if (validated_only && !peer.is_handshake_completed()) {
|
|
2156
|
-
continue;
|
|
2157
|
-
}
|
|
2158
|
-
targets.push_back({peer.socket, peer.peer_id,
|
|
2159
|
-
peer.is_noise_encrypted() ? peer.send_cipher : nullptr});
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
int sent_count = 0;
|
|
2164
|
-
for (const auto& t : targets) {
|
|
2165
|
-
if (send_binary_to_peer_unlocked(t.socket, binary_data, MessageDataType::JSON, t.send_cipher, t.peer_id)) {
|
|
2166
|
-
sent_count++;
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
return sent_count;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
// Specific message creation functions
|
|
2173
|
-
nlohmann::json RatsClient::create_peer_exchange_message(const RatsPeer& peer) {
|
|
2174
|
-
// Create peer exchange payload
|
|
2175
|
-
nlohmann::json payload;
|
|
2176
|
-
payload["ip"] = peer.ip;
|
|
2177
|
-
payload["port"] = peer.port;
|
|
2178
|
-
payload["peer_id"] = peer.peer_id;
|
|
2179
|
-
payload["connection_type"] = peer.is_outgoing ? "outgoing" : "incoming";
|
|
2180
|
-
|
|
2181
|
-
// Create rats message - use OUR peer_id as sender, not the advertised peer's id
|
|
2182
|
-
return create_rats_message("peer", payload, get_our_peer_id());
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
void RatsClient::broadcast_peer_exchange_message(const RatsPeer& new_peer) {
|
|
2186
|
-
// Don't broadcast exchange messages for ourselves
|
|
2187
|
-
if (new_peer.peer_id.empty()) {
|
|
2188
|
-
return;
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
// Create peer exchange message
|
|
2192
|
-
nlohmann::json message = create_peer_exchange_message(new_peer);
|
|
2193
|
-
|
|
2194
|
-
// Broadcast to all validated peers except the new peer
|
|
2195
|
-
int sent_count = broadcast_rats_message(message, new_peer.peer_id);
|
|
2196
|
-
|
|
2197
|
-
LOG_CLIENT_INFO("Broadcasted peer exchange message for " << new_peer.ip << ":" << new_peer.port
|
|
2198
|
-
<< " to " << sent_count << " peers");
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
// Peers request/response system implementation
|
|
2202
|
-
nlohmann::json RatsClient::create_peers_request_message(const std::string& sender_peer_id) {
|
|
2203
|
-
nlohmann::json payload;
|
|
2204
|
-
payload["max_peers"] = MAX_PEERS_REQUEST_COUNT;
|
|
2205
|
-
payload["requester_info"] = {
|
|
2206
|
-
{"listen_port", listen_port_},
|
|
2207
|
-
{"peer_count", get_peer_count()}
|
|
2208
|
-
};
|
|
2209
|
-
|
|
2210
|
-
return create_rats_message("peers_request", payload, sender_peer_id);
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
nlohmann::json RatsClient::create_peers_response_message(const std::vector<RatsPeer>& peers, const std::string& sender_peer_id) {
|
|
2214
|
-
nlohmann::json payload;
|
|
2215
|
-
nlohmann::json peers_array = nlohmann::json::array();
|
|
2216
|
-
|
|
2217
|
-
for (const auto& peer : peers) {
|
|
2218
|
-
peers_array.push_back({
|
|
2219
|
-
{"ip", peer.ip},
|
|
2220
|
-
{"port", peer.port},
|
|
2221
|
-
{"peer_id", peer.peer_id},
|
|
2222
|
-
{"connection_type", peer.is_outgoing ? "outgoing" : "incoming"}
|
|
2223
|
-
});
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
payload["peers"] = std::move(peers_array);
|
|
2227
|
-
payload["total_peers"] = get_peer_count();
|
|
2228
|
-
|
|
2229
|
-
return create_rats_message("peers_response", payload, sender_peer_id);
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
void RatsClient::handle_peers_request_message(socket_t socket, const std::string& peer_id, const nlohmann::json& payload) {
|
|
2233
|
-
try {
|
|
2234
|
-
int max_peers = payload.value("max_peers", MAX_PEERS_REQUEST_COUNT);
|
|
2235
|
-
|
|
2236
|
-
LOG_CLIENT_INFO("Received peers request from " << peer_id << " for up to " << max_peers << " peers");
|
|
2237
|
-
|
|
2238
|
-
// Get random peers excluding the requester
|
|
2239
|
-
std::vector<RatsPeer> random_peers = get_random_peers(max_peers, peer_id);
|
|
2240
|
-
|
|
2241
|
-
LOG_CLIENT_DEBUG("Sending " << random_peers.size() << " peers to " << peer_id);
|
|
2242
|
-
|
|
2243
|
-
// Create and send peers response
|
|
2244
|
-
nlohmann::json response_message = create_peers_response_message(random_peers, peer_id);
|
|
2245
|
-
|
|
2246
|
-
if (!send_json_to_peer(socket, response_message)) {
|
|
2247
|
-
LOG_CLIENT_ERROR("Failed to send peers response to " << peer_id);
|
|
2248
|
-
} else {
|
|
2249
|
-
LOG_CLIENT_DEBUG("Sent peers response with " << random_peers.size() << " peers to " << peer_id);
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
} catch (const nlohmann::json::exception& e) {
|
|
2253
|
-
LOG_CLIENT_ERROR("Failed to handle peers request message: " << e.what());
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
void RatsClient::handle_peers_response_message(socket_t socket, const std::string& peer_id, const nlohmann::json& payload) {
|
|
2258
|
-
try {
|
|
2259
|
-
nlohmann::json peers_array = payload.value("peers", nlohmann::json::array());
|
|
2260
|
-
int total_peers = payload.value("total_peers", 0);
|
|
2261
|
-
|
|
2262
|
-
LOG_CLIENT_INFO("Received peers response from " << peer_id << " with " << peers_array.size()
|
|
2263
|
-
<< " peers (total: " << total_peers << ")");
|
|
2264
|
-
|
|
2265
|
-
// Process each peer in the response
|
|
2266
|
-
for (const auto& peer_info : peers_array) {
|
|
2267
|
-
std::string resp_ip = peer_info.value("ip", "");
|
|
2268
|
-
int resp_port = peer_info.value("port", 0);
|
|
2269
|
-
std::string resp_peer_id = peer_info.value("peer_id", "");
|
|
2270
|
-
|
|
2271
|
-
if (resp_ip.empty() || resp_port <= 0 || resp_peer_id.empty()) {
|
|
2272
|
-
LOG_CLIENT_WARN("Invalid peer info in peers response from " << peer_id);
|
|
2273
|
-
continue;
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
LOG_CLIENT_DEBUG("Processing peer from response: " << resp_ip << ":" << resp_port << " (peer_id: " << resp_peer_id << ")");
|
|
2277
|
-
|
|
2278
|
-
if (!can_connect_to_peer(resp_ip, resp_port)) {
|
|
2279
|
-
continue;
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
LOG_CLIENT_DEBUG("Attempting to connect to peer from response: " << resp_ip << ":" << resp_port);
|
|
2283
|
-
add_managed_thread(std::thread([this, resp_ip, resp_port, resp_peer_id]() {
|
|
2284
|
-
if (connect_to_peer(resp_ip, resp_port)) {
|
|
2285
|
-
LOG_CLIENT_INFO("Successfully connected to peer from response: " << resp_ip << ":" << resp_port);
|
|
2286
|
-
} else {
|
|
2287
|
-
LOG_CLIENT_DEBUG("Failed to connect to peer from response: " << resp_ip << ":" << resp_port);
|
|
2288
|
-
}
|
|
2289
|
-
}), "peer-response-connect-" + resp_peer_id.substr(0, 8));
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
} catch (const nlohmann::json::exception& e) {
|
|
2293
|
-
LOG_CLIENT_ERROR("Failed to handle peers response message: " << e.what());
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
|
|
2297
|
-
void RatsClient::send_peers_request(socket_t socket, const std::string& our_peer_id) {
|
|
2298
|
-
nlohmann::json request_message = create_peers_request_message(our_peer_id);
|
|
2299
|
-
|
|
2300
|
-
if (send_json_to_peer(socket, request_message)) {
|
|
2301
|
-
LOG_CLIENT_INFO("Sent peers request to socket " << socket);
|
|
2302
|
-
} else {
|
|
2303
|
-
LOG_CLIENT_ERROR("Failed to send peers request to socket " << socket);
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
// =========================================================================
|
|
2308
|
-
// Helper Functions
|
|
2309
|
-
// =========================================================================
|
|
2310
|
-
|
|
2311
|
-
std::unique_ptr<RatsClient> create_rats_client(int listen_port) {
|
|
2312
|
-
auto client = std::make_unique<RatsClient>(listen_port, 10); // Default 10 max peers
|
|
2313
|
-
if (!client->start()) {
|
|
2314
|
-
return nullptr;
|
|
2315
|
-
}
|
|
2316
|
-
return client;
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
|
-
// Version query functions
|
|
2320
|
-
const char* rats_get_library_version_string() {
|
|
2321
|
-
return librats::version::STRING;
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
void rats_get_library_version(int* major, int* minor, int* patch, int* build) {
|
|
2325
|
-
if (major) *major = librats::version::MAJOR;
|
|
2326
|
-
if (minor) *minor = librats::version::MINOR;
|
|
2327
|
-
if (patch) *patch = librats::version::PATCH;
|
|
2328
|
-
if (build) *build = librats::version::BUILD;
|
|
2329
|
-
}
|
|
2330
|
-
|
|
2331
|
-
const char* rats_get_library_git_describe() {
|
|
2332
|
-
return librats::version::GIT_DESCRIBE;
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
uint32_t rats_get_library_abi() {
|
|
2336
|
-
// ABI policy: MAJOR bumps on breaking changes; MINOR for additive; PATCH ignored in ABI id
|
|
2337
|
-
return (static_cast<uint32_t>(librats::version::MAJOR) << 16) |
|
|
2338
|
-
(static_cast<uint32_t>(librats::version::MINOR) << 8) |
|
|
2339
|
-
(static_cast<uint32_t>(librats::version::PATCH));
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
bool RatsClient::parse_address_string(const std::string& address_str, std::string& out_ip, int& out_port) {
|
|
2343
|
-
if (address_str.empty()) {
|
|
2344
|
-
return false;
|
|
2345
|
-
}
|
|
2346
|
-
|
|
2347
|
-
size_t colon_pos;
|
|
2348
|
-
if (address_str.front() == '[') {
|
|
2349
|
-
// IPv6 format: [ip]:port
|
|
2350
|
-
size_t bracket_end = address_str.find(']');
|
|
2351
|
-
if (bracket_end == std::string::npos || bracket_end < 2) { // Must be at least [a]
|
|
2352
|
-
return false;
|
|
2353
|
-
}
|
|
2354
|
-
out_ip = address_str.substr(1, bracket_end - 1);
|
|
2355
|
-
colon_pos = address_str.find(':', bracket_end);
|
|
2356
|
-
} else {
|
|
2357
|
-
// IPv4 or IPv6 without brackets
|
|
2358
|
-
colon_pos = address_str.find_last_of(':');
|
|
2359
|
-
if (colon_pos == std::string::npos || colon_pos == 0) {
|
|
2360
|
-
return false;
|
|
2361
|
-
}
|
|
2362
|
-
out_ip = address_str.substr(0, colon_pos);
|
|
2363
|
-
}
|
|
2364
|
-
|
|
2365
|
-
if (colon_pos == std::string::npos || colon_pos + 1 >= address_str.length()) {
|
|
2366
|
-
return false;
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
try {
|
|
2370
|
-
out_port = std::stoi(address_str.substr(colon_pos + 1));
|
|
2371
|
-
} catch (const std::exception&) {
|
|
2372
|
-
return false;
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
|
-
return !out_ip.empty() && out_port > 0 && out_port <= 65535;
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
} // namespace librats
|