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,14 +1,34 @@
|
|
|
1
|
-
#include "socket.h"
|
|
2
|
-
#include "network_utils.h"
|
|
3
|
-
#include "logger.h"
|
|
1
|
+
#include "librats/core/socket.h"
|
|
2
|
+
#include "librats/util/network_utils.h"
|
|
3
|
+
#include "librats/util/logger.h"
|
|
4
4
|
#include <iostream>
|
|
5
5
|
#include <cstring>
|
|
6
6
|
#include <mutex>
|
|
7
7
|
#include <thread>
|
|
8
8
|
#include <chrono>
|
|
9
9
|
#ifndef _WIN32
|
|
10
|
-
#include <fcntl.h>
|
|
11
|
-
#include <errno.h>
|
|
10
|
+
#include <fcntl.h> // for O_NONBLOCK
|
|
11
|
+
#include <errno.h> // for errno
|
|
12
|
+
#include <sys/uio.h> // for iovec (send_vectored)
|
|
13
|
+
#include <limits.h> // for IOV_MAX (send_vectored)
|
|
14
|
+
#include <netinet/in.h> // for IPPROTO_TCP (set_tcp_nodelay)
|
|
15
|
+
#include <netinet/tcp.h> // for TCP_NODELAY
|
|
16
|
+
// macOS/BSD have no MSG_NOSIGNAL; they suppress SIGPIPE with the SO_NOSIGPIPE
|
|
17
|
+
// socket option instead, which suppress_sigpipe() sets on every TCP socket we
|
|
18
|
+
// send on — so sending with no flag is the right fallback there.
|
|
19
|
+
#ifndef MSG_NOSIGNAL
|
|
20
|
+
#define MSG_NOSIGNAL 0
|
|
21
|
+
#endif
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
// RATS_HAVE_MMSG (recvmmsg/sendmmsg availability) is decided in socket.h, next to
|
|
25
|
+
// the kUdpBatchIsOneSyscall it publishes — callers outside this file need to know
|
|
26
|
+
// whether a batch is genuinely one syscall.
|
|
27
|
+
|
|
28
|
+
// On Windows, SIO_UDP_CONNRESET lives in <mstcpip.h>, which mingw doesn't always pull
|
|
29
|
+
// in via <winsock2.h>. Define it from its well-known control code as a fallback.
|
|
30
|
+
#if defined(_WIN32) && !defined(SIO_UDP_CONNRESET)
|
|
31
|
+
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
|
|
12
32
|
#endif
|
|
13
33
|
|
|
14
34
|
// Socket module logging macros
|
|
@@ -45,33 +65,71 @@ static std::string socket_error_string(int error) {
|
|
|
45
65
|
#endif
|
|
46
66
|
}
|
|
47
67
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
static void set_last_socket_error(int error) {
|
|
69
|
+
#ifdef _WIN32
|
|
70
|
+
WSASetLastError(error);
|
|
71
|
+
#else
|
|
72
|
+
errno = error;
|
|
73
|
+
#endif
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Put a failing socket back in the caller's hands with the error that actually
|
|
78
|
+
* caused it. close_socket() is a syscall like any other and overwrites errno on
|
|
79
|
+
* its way out, so a factory that cleans up before returning would otherwise hand
|
|
80
|
+
* the caller the close()'s error — or a stale success.
|
|
81
|
+
*/
|
|
82
|
+
static socket_t fail_socket(socket_t socket, int error) {
|
|
83
|
+
close_socket(socket);
|
|
84
|
+
set_last_socket_error(error);
|
|
85
|
+
return RATS_INVALID_SOCKET;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
bool last_error_was_port_unavailable() {
|
|
89
|
+
#ifdef _WIN32
|
|
90
|
+
/*
|
|
91
|
+
* WSAEACCES from a bind is not "denied" in the POSIX sense: it is how
|
|
92
|
+
* Windows reports a port inside a reserved range — the blocks Hyper-V,
|
|
93
|
+
* WinNAT and WSL2 carve out of the ephemeral range, or a port another
|
|
94
|
+
* process holds with SO_EXCLUSIVEADDRUSE. Like a clash, it is answered by
|
|
95
|
+
* moving to another port, and it has to be, because the TCP and UDP
|
|
96
|
+
* exclusion ranges are separate: a stream socket lands on a port the
|
|
97
|
+
* matching datagram bind is then refused.
|
|
98
|
+
*/
|
|
99
|
+
const int error = get_last_socket_error();
|
|
100
|
+
return error == WSAEADDRINUSE || error == WSAEACCES;
|
|
101
|
+
#else
|
|
102
|
+
// No POSIX counterpart worth adding: EACCES there means a privileged port,
|
|
103
|
+
// which the next few ports up would be refused for just the same.
|
|
104
|
+
return get_last_socket_error() == EADDRINUSE;
|
|
105
|
+
#endif
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
void suppress_sigpipe(socket_t socket) {
|
|
109
|
+
#ifdef SO_NOSIGPIPE
|
|
110
|
+
int on = 1;
|
|
111
|
+
if (setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE,
|
|
112
|
+
reinterpret_cast<const char*>(&on), sizeof(on)) != 0) {
|
|
113
|
+
LOG_SOCKET_WARN("Failed to set SO_NOSIGPIPE on socket " << socket << ": "
|
|
114
|
+
<< socket_error_string(get_last_socket_error()));
|
|
115
|
+
}
|
|
116
|
+
#else
|
|
117
|
+
(void)socket;
|
|
118
|
+
#endif
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Extract sender peer info from sockaddr_storage (shared by UDP receive).
|
|
122
|
+
// IpAddress::from_sockaddr copies the raw address bytes (and unwraps IPv4-mapped
|
|
123
|
+
// IPv6) with no textual round-trip; an unknown family yields an unspecified peer.
|
|
124
|
+
static void extract_sender_peer(const sockaddr_storage& sender_addr, Address& peer) {
|
|
125
|
+
const auto* sa = reinterpret_cast<const sockaddr*>(&sender_addr);
|
|
126
|
+
if (auto ip = IpAddress::from_sockaddr(sa)) {
|
|
127
|
+
peer.ip = *ip;
|
|
128
|
+
peer.port = (sender_addr.ss_family == AF_INET)
|
|
129
|
+
? ntohs(reinterpret_cast<const sockaddr_in*>(&sender_addr)->sin_port)
|
|
130
|
+
: ntohs(reinterpret_cast<const sockaddr_in6*>(&sender_addr)->sin6_port);
|
|
72
131
|
} else {
|
|
73
|
-
peer
|
|
74
|
-
peer.port = 0;
|
|
132
|
+
peer = Address{};
|
|
75
133
|
}
|
|
76
134
|
}
|
|
77
135
|
|
|
@@ -81,10 +139,11 @@ static socket_t create_tcp_client_v4(const std::string& host, int port, int time
|
|
|
81
139
|
LOG_SOCKET_DEBUG("Creating TCP client socket (IPv4) for " << host << ":" << port);
|
|
82
140
|
|
|
83
141
|
socket_t client_socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
84
|
-
if (client_socket ==
|
|
142
|
+
if (client_socket == RATS_INVALID_SOCKET) {
|
|
85
143
|
LOG_SOCKET_ERROR("Failed to create IPv4 client socket");
|
|
86
|
-
return
|
|
144
|
+
return RATS_INVALID_SOCKET;
|
|
87
145
|
}
|
|
146
|
+
suppress_sigpipe(client_socket);
|
|
88
147
|
|
|
89
148
|
sockaddr_in server_addr;
|
|
90
149
|
memset(&server_addr, 0, sizeof(server_addr));
|
|
@@ -95,13 +154,13 @@ static socket_t create_tcp_client_v4(const std::string& host, int port, int time
|
|
|
95
154
|
if (resolved_ip.empty()) {
|
|
96
155
|
LOG_SOCKET_ERROR("Failed to resolve hostname: " << host);
|
|
97
156
|
close_socket(client_socket);
|
|
98
|
-
return
|
|
157
|
+
return RATS_INVALID_SOCKET;
|
|
99
158
|
}
|
|
100
159
|
|
|
101
160
|
if (inet_pton(AF_INET, resolved_ip.c_str(), &server_addr.sin_addr) <= 0) {
|
|
102
161
|
LOG_SOCKET_ERROR("Invalid address: " << resolved_ip);
|
|
103
162
|
close_socket(client_socket);
|
|
104
|
-
return
|
|
163
|
+
return RATS_INVALID_SOCKET;
|
|
105
164
|
}
|
|
106
165
|
|
|
107
166
|
LOG_SOCKET_DEBUG("Connecting to " << resolved_ip << ":" << port);
|
|
@@ -111,13 +170,13 @@ static socket_t create_tcp_client_v4(const std::string& host, int port, int time
|
|
|
111
170
|
sizeof(server_addr), timeout_ms);
|
|
112
171
|
} else {
|
|
113
172
|
ok = (connect(client_socket, reinterpret_cast<sockaddr*>(&server_addr),
|
|
114
|
-
sizeof(server_addr)) !=
|
|
173
|
+
sizeof(server_addr)) != RATS_SOCKET_ERROR);
|
|
115
174
|
}
|
|
116
175
|
|
|
117
176
|
if (!ok) {
|
|
118
177
|
LOG_SOCKET_DEBUG("Connection to " << resolved_ip << ":" << port << " failed");
|
|
119
178
|
close_socket(client_socket);
|
|
120
|
-
return
|
|
179
|
+
return RATS_INVALID_SOCKET;
|
|
121
180
|
}
|
|
122
181
|
|
|
123
182
|
LOG_SOCKET_INFO("Successfully connected to " << resolved_ip << ":" << port);
|
|
@@ -128,10 +187,11 @@ static socket_t create_tcp_client_v6(const std::string& host, int port, int time
|
|
|
128
187
|
LOG_SOCKET_DEBUG("Creating TCP client socket (IPv6) for " << host << ":" << port);
|
|
129
188
|
|
|
130
189
|
socket_t client_socket = socket(AF_INET6, SOCK_STREAM, 0);
|
|
131
|
-
if (client_socket ==
|
|
190
|
+
if (client_socket == RATS_INVALID_SOCKET) {
|
|
132
191
|
LOG_SOCKET_ERROR("Failed to create IPv6 client socket");
|
|
133
|
-
return
|
|
192
|
+
return RATS_INVALID_SOCKET;
|
|
134
193
|
}
|
|
194
|
+
suppress_sigpipe(client_socket);
|
|
135
195
|
|
|
136
196
|
sockaddr_in6 server_addr;
|
|
137
197
|
memset(&server_addr, 0, sizeof(server_addr));
|
|
@@ -142,13 +202,13 @@ static socket_t create_tcp_client_v6(const std::string& host, int port, int time
|
|
|
142
202
|
if (resolved_ip.empty()) {
|
|
143
203
|
LOG_SOCKET_DEBUG("Failed to resolve hostname to IPv6: " << host);
|
|
144
204
|
close_socket(client_socket);
|
|
145
|
-
return
|
|
205
|
+
return RATS_INVALID_SOCKET;
|
|
146
206
|
}
|
|
147
207
|
|
|
148
208
|
if (inet_pton(AF_INET6, resolved_ip.c_str(), &server_addr.sin6_addr) <= 0) {
|
|
149
209
|
LOG_SOCKET_ERROR("Invalid IPv6 address: " << resolved_ip);
|
|
150
210
|
close_socket(client_socket);
|
|
151
|
-
return
|
|
211
|
+
return RATS_INVALID_SOCKET;
|
|
152
212
|
}
|
|
153
213
|
|
|
154
214
|
LOG_SOCKET_DEBUG("Connecting to IPv6 " << resolved_ip << ":" << port);
|
|
@@ -158,13 +218,13 @@ static socket_t create_tcp_client_v6(const std::string& host, int port, int time
|
|
|
158
218
|
sizeof(server_addr), timeout_ms);
|
|
159
219
|
} else {
|
|
160
220
|
ok = (connect(client_socket, reinterpret_cast<sockaddr*>(&server_addr),
|
|
161
|
-
sizeof(server_addr)) !=
|
|
221
|
+
sizeof(server_addr)) != RATS_SOCKET_ERROR);
|
|
162
222
|
}
|
|
163
223
|
|
|
164
224
|
if (!ok) {
|
|
165
225
|
LOG_SOCKET_DEBUG("Connection to IPv6 " << resolved_ip << ":" << port << " failed");
|
|
166
226
|
close_socket(client_socket);
|
|
167
|
-
return
|
|
227
|
+
return RATS_INVALID_SOCKET;
|
|
168
228
|
}
|
|
169
229
|
|
|
170
230
|
LOG_SOCKET_INFO("Successfully connected to IPv6 " << resolved_ip << ":" << port);
|
|
@@ -293,16 +353,102 @@ bool connect_with_timeout(socket_t socket, struct sockaddr* addr, socklen_t addr
|
|
|
293
353
|
return false;
|
|
294
354
|
}
|
|
295
355
|
|
|
356
|
+
// ── Non-blocking connect (reactor-driven) ───────────────────────────────────
|
|
357
|
+
|
|
358
|
+
static socket_t tcp_connect_start_family(int family, const std::string& resolved_ip, int port) {
|
|
359
|
+
socket_t s = socket(family, SOCK_STREAM, 0);
|
|
360
|
+
if (s == RATS_INVALID_SOCKET) return RATS_INVALID_SOCKET;
|
|
361
|
+
suppress_sigpipe(s);
|
|
362
|
+
|
|
363
|
+
if (!set_socket_nonblocking(s)) {
|
|
364
|
+
close_socket(s);
|
|
365
|
+
return RATS_INVALID_SOCKET;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
sockaddr_storage ss;
|
|
369
|
+
memset(&ss, 0, sizeof(ss));
|
|
370
|
+
socklen_t addr_len = 0;
|
|
371
|
+
|
|
372
|
+
if (family == AF_INET) {
|
|
373
|
+
auto* a = reinterpret_cast<sockaddr_in*>(&ss);
|
|
374
|
+
a->sin_family = AF_INET;
|
|
375
|
+
a->sin_port = htons(static_cast<uint16_t>(port));
|
|
376
|
+
if (inet_pton(AF_INET, resolved_ip.c_str(), &a->sin_addr) <= 0) {
|
|
377
|
+
close_socket(s);
|
|
378
|
+
return RATS_INVALID_SOCKET;
|
|
379
|
+
}
|
|
380
|
+
addr_len = sizeof(sockaddr_in);
|
|
381
|
+
} else {
|
|
382
|
+
auto* a = reinterpret_cast<sockaddr_in6*>(&ss);
|
|
383
|
+
a->sin6_family = AF_INET6;
|
|
384
|
+
a->sin6_port = htons(static_cast<uint16_t>(port));
|
|
385
|
+
if (inet_pton(AF_INET6, resolved_ip.c_str(), &a->sin6_addr) <= 0) {
|
|
386
|
+
close_socket(s);
|
|
387
|
+
return RATS_INVALID_SOCKET;
|
|
388
|
+
}
|
|
389
|
+
addr_len = sizeof(sockaddr_in6);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
int r = connect(s, reinterpret_cast<sockaddr*>(&ss), addr_len);
|
|
393
|
+
if (r == 0) {
|
|
394
|
+
return s; // Connected synchronously (common on loopback).
|
|
395
|
+
}
|
|
396
|
+
#ifdef _WIN32
|
|
397
|
+
int e = WSAGetLastError();
|
|
398
|
+
if (e == WSAEWOULDBLOCK || e == WSAEINPROGRESS) return s; // In progress.
|
|
399
|
+
#else
|
|
400
|
+
if (errno == EINPROGRESS) return s; // In progress.
|
|
401
|
+
#endif
|
|
402
|
+
LOG_SOCKET_DEBUG("Non-blocking connect to " << resolved_ip << ":" << port << " failed to start");
|
|
403
|
+
close_socket(s);
|
|
404
|
+
return RATS_INVALID_SOCKET;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
socket_t tcp_connect_start(const std::string& host, int port) {
|
|
408
|
+
if (!validate_port(port)) return RATS_INVALID_SOCKET;
|
|
409
|
+
|
|
410
|
+
// Prefer IPv6, fall back to IPv4 — same precedence as create_tcp_client().
|
|
411
|
+
// Note: fallback happens at *resolution* time; a v6 address that resolves
|
|
412
|
+
// but fails asynchronously surfaces later as ConnectFailed (happy-eyeballs
|
|
413
|
+
// sequencing belongs in the higher-level dialer).
|
|
414
|
+
std::string ip6 = network_utils::resolve_hostname_v6(host);
|
|
415
|
+
if (!ip6.empty()) {
|
|
416
|
+
socket_t s = tcp_connect_start_family(AF_INET6, ip6, port);
|
|
417
|
+
if (is_valid_socket(s)) return s;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
std::string ip4 = network_utils::resolve_hostname(host);
|
|
421
|
+
if (!ip4.empty()) {
|
|
422
|
+
socket_t s = tcp_connect_start_family(AF_INET, ip4, port);
|
|
423
|
+
if (is_valid_socket(s)) return s;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return RATS_INVALID_SOCKET;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
int tcp_connect_result(socket_t socket) {
|
|
430
|
+
int err = 0;
|
|
431
|
+
socklen_t len = sizeof(err);
|
|
432
|
+
if (getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &len) != 0) {
|
|
433
|
+
#ifdef _WIN32
|
|
434
|
+
return WSAGetLastError();
|
|
435
|
+
#else
|
|
436
|
+
return errno;
|
|
437
|
+
#endif
|
|
438
|
+
}
|
|
439
|
+
return err; // 0 == connected
|
|
440
|
+
}
|
|
441
|
+
|
|
296
442
|
// ── TCP Socket Functions ────────────────────────────────────────────────────
|
|
297
443
|
|
|
298
444
|
socket_t create_tcp_client(const std::string& host, int port, int timeout_ms) {
|
|
299
|
-
if (!validate_port(port)) return
|
|
445
|
+
if (!validate_port(port)) return RATS_INVALID_SOCKET;
|
|
300
446
|
|
|
301
447
|
LOG_SOCKET_DEBUG("Creating TCP client socket (dual stack) for " << host << ":" << port);
|
|
302
448
|
|
|
303
449
|
// Try IPv6 first
|
|
304
450
|
socket_t client_socket = create_tcp_client_v6(host, port, timeout_ms);
|
|
305
|
-
if (client_socket !=
|
|
451
|
+
if (client_socket != RATS_INVALID_SOCKET) {
|
|
306
452
|
LOG_SOCKET_INFO("Successfully connected using IPv6");
|
|
307
453
|
return client_socket;
|
|
308
454
|
}
|
|
@@ -310,17 +456,17 @@ socket_t create_tcp_client(const std::string& host, int port, int timeout_ms) {
|
|
|
310
456
|
// Fall back to IPv4
|
|
311
457
|
LOG_SOCKET_DEBUG("IPv6 connection failed, trying IPv4");
|
|
312
458
|
client_socket = create_tcp_client_v4(host, port, timeout_ms);
|
|
313
|
-
if (client_socket !=
|
|
459
|
+
if (client_socket != RATS_INVALID_SOCKET) {
|
|
314
460
|
LOG_SOCKET_INFO("Successfully connected using IPv4");
|
|
315
461
|
return client_socket;
|
|
316
462
|
}
|
|
317
463
|
|
|
318
464
|
LOG_SOCKET_DEBUG("Failed to connect using both IPv6 and IPv4");
|
|
319
|
-
return
|
|
465
|
+
return RATS_INVALID_SOCKET;
|
|
320
466
|
}
|
|
321
467
|
|
|
322
468
|
socket_t create_tcp_server(int port, int backlog, const std::string& bind_address, AddressFamily af) {
|
|
323
|
-
if (!validate_port(port)) return
|
|
469
|
+
if (!validate_port(port)) return RATS_INVALID_SOCKET;
|
|
324
470
|
|
|
325
471
|
const char* af_label = (af == AddressFamily::IPv4) ? "IPv4" :
|
|
326
472
|
(af == AddressFamily::IPv6) ? "IPv6" : "dual stack";
|
|
@@ -330,27 +476,30 @@ socket_t create_tcp_server(int port, int backlog, const std::string& bind_addres
|
|
|
330
476
|
int family = (af == AddressFamily::IPv4) ? AF_INET : AF_INET6;
|
|
331
477
|
|
|
332
478
|
socket_t server_socket = socket(family, SOCK_STREAM, 0);
|
|
333
|
-
if (server_socket ==
|
|
334
|
-
LOG_SOCKET_ERROR("Failed to create " << af_label << " server socket"
|
|
335
|
-
|
|
479
|
+
if (server_socket == RATS_INVALID_SOCKET) {
|
|
480
|
+
LOG_SOCKET_ERROR("Failed to create " << af_label << " server socket (error: "
|
|
481
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
482
|
+
return RATS_INVALID_SOCKET;
|
|
336
483
|
}
|
|
337
484
|
|
|
338
485
|
// Set socket option to reuse address
|
|
339
486
|
int opt = 1;
|
|
340
487
|
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
|
|
341
|
-
(char*)&opt, sizeof(opt)) ==
|
|
342
|
-
LOG_SOCKET_ERROR("Failed to set " << af_label << " socket options"
|
|
488
|
+
(char*)&opt, sizeof(opt)) == RATS_SOCKET_ERROR) {
|
|
489
|
+
LOG_SOCKET_ERROR("Failed to set " << af_label << " socket options (error: "
|
|
490
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
343
491
|
close_socket(server_socket);
|
|
344
|
-
return
|
|
492
|
+
return RATS_INVALID_SOCKET;
|
|
345
493
|
}
|
|
346
494
|
|
|
347
495
|
// For IPv6/DualStack sockets, configure IPV6_V6ONLY
|
|
348
496
|
if (family == AF_INET6) {
|
|
349
497
|
int ipv6_only = (af == AddressFamily::IPv6) ? 1 : 0;
|
|
350
498
|
if (setsockopt(server_socket, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
351
|
-
(char*)&ipv6_only, sizeof(ipv6_only)) ==
|
|
499
|
+
(char*)&ipv6_only, sizeof(ipv6_only)) == RATS_SOCKET_ERROR) {
|
|
352
500
|
if (af == AddressFamily::DualStack) {
|
|
353
|
-
LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only"
|
|
501
|
+
LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only (error: "
|
|
502
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
354
503
|
}
|
|
355
504
|
}
|
|
356
505
|
}
|
|
@@ -368,15 +517,16 @@ socket_t create_tcp_server(int port, int backlog, const std::string& bind_addres
|
|
|
368
517
|
if (inet_pton(AF_INET, bind_address.c_str(), &server_addr.sin_addr) != 1) {
|
|
369
518
|
LOG_SOCKET_ERROR("Invalid IPv4 bind address: " << bind_address);
|
|
370
519
|
close_socket(server_socket);
|
|
371
|
-
return
|
|
520
|
+
return RATS_INVALID_SOCKET;
|
|
372
521
|
}
|
|
373
522
|
}
|
|
374
523
|
|
|
375
524
|
if (bind(server_socket, reinterpret_cast<sockaddr*>(&server_addr),
|
|
376
|
-
sizeof(server_addr)) ==
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
525
|
+
sizeof(server_addr)) == RATS_SOCKET_ERROR) {
|
|
526
|
+
const int error = get_last_socket_error();
|
|
527
|
+
LOG_SOCKET_ERROR("Failed to bind " << af_label << " server socket to port " << port
|
|
528
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
529
|
+
return fail_socket(server_socket, error);
|
|
380
530
|
}
|
|
381
531
|
} else {
|
|
382
532
|
sockaddr_in6 server_addr;
|
|
@@ -390,22 +540,24 @@ socket_t create_tcp_server(int port, int backlog, const std::string& bind_addres
|
|
|
390
540
|
if (inet_pton(AF_INET6, bind_address.c_str(), &server_addr.sin6_addr) != 1) {
|
|
391
541
|
LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
|
|
392
542
|
close_socket(server_socket);
|
|
393
|
-
return
|
|
543
|
+
return RATS_INVALID_SOCKET;
|
|
394
544
|
}
|
|
395
545
|
}
|
|
396
546
|
|
|
397
547
|
if (bind(server_socket, reinterpret_cast<sockaddr*>(&server_addr),
|
|
398
|
-
sizeof(server_addr)) ==
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
548
|
+
sizeof(server_addr)) == RATS_SOCKET_ERROR) {
|
|
549
|
+
const int error = get_last_socket_error();
|
|
550
|
+
LOG_SOCKET_ERROR("Failed to bind " << af_label << " server socket to port " << port
|
|
551
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
552
|
+
return fail_socket(server_socket, error);
|
|
402
553
|
}
|
|
403
554
|
}
|
|
404
555
|
|
|
405
|
-
if (listen(server_socket, backlog) ==
|
|
406
|
-
LOG_SOCKET_ERROR("Failed to listen on " << af_label << " server socket"
|
|
556
|
+
if (listen(server_socket, backlog) == RATS_SOCKET_ERROR) {
|
|
557
|
+
LOG_SOCKET_ERROR("Failed to listen on " << af_label << " server socket (error: "
|
|
558
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
407
559
|
close_socket(server_socket);
|
|
408
|
-
return
|
|
560
|
+
return RATS_INVALID_SOCKET;
|
|
409
561
|
}
|
|
410
562
|
|
|
411
563
|
LOG_SOCKET_INFO(af_label << " server listening on port " << port << " (backlog: " << backlog << ")");
|
|
@@ -417,10 +569,11 @@ socket_t accept_client(socket_t server_socket) {
|
|
|
417
569
|
socklen_t client_addr_len = sizeof(client_addr);
|
|
418
570
|
|
|
419
571
|
socket_t client_socket = accept(server_socket, reinterpret_cast<sockaddr*>(&client_addr), &client_addr_len);
|
|
420
|
-
if (client_socket ==
|
|
572
|
+
if (client_socket == RATS_INVALID_SOCKET) {
|
|
421
573
|
LOG_SOCKET_ERROR("Failed to accept client connection");
|
|
422
|
-
return
|
|
574
|
+
return RATS_INVALID_SOCKET;
|
|
423
575
|
}
|
|
576
|
+
suppress_sigpipe(client_socket);
|
|
424
577
|
|
|
425
578
|
if (client_addr.ss_family == AF_INET) {
|
|
426
579
|
char client_ip[INET_ADDRSTRLEN];
|
|
@@ -443,7 +596,7 @@ std::string get_peer_address(socket_t socket) {
|
|
|
443
596
|
sockaddr_storage peer_addr;
|
|
444
597
|
socklen_t peer_addr_len = sizeof(peer_addr);
|
|
445
598
|
|
|
446
|
-
if (getpeername(socket, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len) ==
|
|
599
|
+
if (getpeername(socket, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len) == RATS_SOCKET_ERROR) {
|
|
447
600
|
LOG_SOCKET_ERROR("Failed to get peer address for socket " << socket);
|
|
448
601
|
return "";
|
|
449
602
|
}
|
|
@@ -471,6 +624,58 @@ std::string get_peer_address(socket_t socket) {
|
|
|
471
624
|
return peer_ip + ":" + std::to_string(peer_port);
|
|
472
625
|
}
|
|
473
626
|
|
|
627
|
+
std::optional<Address> get_peer_endpoint(socket_t socket) {
|
|
628
|
+
sockaddr_storage ss;
|
|
629
|
+
socklen_t len = sizeof(ss);
|
|
630
|
+
if (getpeername(socket, reinterpret_cast<sockaddr*>(&ss), &len) == RATS_SOCKET_ERROR)
|
|
631
|
+
return std::nullopt;
|
|
632
|
+
auto ip = IpAddress::from_sockaddr(reinterpret_cast<sockaddr*>(&ss));
|
|
633
|
+
if (!ip) return std::nullopt;
|
|
634
|
+
const uint16_t port = (ss.ss_family == AF_INET)
|
|
635
|
+
? ntohs(reinterpret_cast<sockaddr_in*>(&ss)->sin_port)
|
|
636
|
+
: ntohs(reinterpret_cast<sockaddr_in6*>(&ss)->sin6_port);
|
|
637
|
+
return Address{*ip, port};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
std::ptrdiff_t send_vectored(socket_t socket, const ByteView* slices, size_t count) {
|
|
641
|
+
if (count == 0) return 0;
|
|
642
|
+
if (count > kMaxSendSlices) count = kMaxSendSlices;
|
|
643
|
+
#if !defined(_WIN32) && defined(IOV_MAX)
|
|
644
|
+
// sendmsg() rejects an iovec longer than IOV_MAX with EINVAL — which the callers
|
|
645
|
+
// read as "the peer is gone" and would close a perfectly healthy connection over.
|
|
646
|
+
// POSIX only guarantees 16 (Linux and macOS allow 1024), so clamp rather than
|
|
647
|
+
// trust kMaxSendSlices to be under every platform's limit. The slices that don't
|
|
648
|
+
// fit are not lost: they go out on the next round of the caller's flush loop.
|
|
649
|
+
if (count > static_cast<size_t>(IOV_MAX)) count = static_cast<size_t>(IOV_MAX);
|
|
650
|
+
#endif
|
|
651
|
+
|
|
652
|
+
#ifdef _WIN32
|
|
653
|
+
WSABUF bufs[kMaxSendSlices];
|
|
654
|
+
for (size_t i = 0; i < count; ++i) {
|
|
655
|
+
bufs[i].buf = reinterpret_cast<CHAR*>(const_cast<uint8_t*>(slices[i].data()));
|
|
656
|
+
bufs[i].len = static_cast<ULONG>(slices[i].size());
|
|
657
|
+
}
|
|
658
|
+
DWORD sent = 0;
|
|
659
|
+
if (WSASend(socket, bufs, static_cast<DWORD>(count), &sent, 0, nullptr, nullptr) == SOCKET_ERROR) {
|
|
660
|
+
return -1;
|
|
661
|
+
}
|
|
662
|
+
return static_cast<std::ptrdiff_t>(sent);
|
|
663
|
+
#else
|
|
664
|
+
struct iovec iov[kMaxSendSlices];
|
|
665
|
+
for (size_t i = 0; i < count; ++i) {
|
|
666
|
+
iov[i].iov_base = const_cast<uint8_t*>(slices[i].data());
|
|
667
|
+
iov[i].iov_len = slices[i].size();
|
|
668
|
+
}
|
|
669
|
+
struct msghdr msg = {};
|
|
670
|
+
msg.msg_iov = iov;
|
|
671
|
+
msg.msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(count);
|
|
672
|
+
// MSG_NOSIGNAL keeps a peer that hung up from killing the process with SIGPIPE.
|
|
673
|
+
// It doesn't exist on macOS/BSD (they use SO_NOSIGPIPE on the socket instead),
|
|
674
|
+
// where the fallback below makes this a plain sendmsg().
|
|
675
|
+
return ::sendmsg(socket, &msg, MSG_NOSIGNAL);
|
|
676
|
+
#endif
|
|
677
|
+
}
|
|
678
|
+
|
|
474
679
|
int send_tcp_data(socket_t socket, const std::vector<uint8_t>& data) {
|
|
475
680
|
LOG_SOCKET_DEBUG("Sending " << data.size() << " bytes to TCP socket " << socket);
|
|
476
681
|
|
|
@@ -484,7 +689,7 @@ int send_tcp_data(socket_t socket, const std::vector<uint8_t>& data) {
|
|
|
484
689
|
#else
|
|
485
690
|
int bytes_sent = send(socket, buffer + total_sent, remaining, MSG_NOSIGNAL);
|
|
486
691
|
#endif
|
|
487
|
-
if (bytes_sent ==
|
|
692
|
+
if (bytes_sent == RATS_SOCKET_ERROR) {
|
|
488
693
|
int error = get_last_socket_error();
|
|
489
694
|
#ifdef _WIN32
|
|
490
695
|
if (error == WSAEWOULDBLOCK) { continue; }
|
|
@@ -523,7 +728,7 @@ std::vector<uint8_t> receive_tcp_data(socket_t socket, size_t buffer_size) {
|
|
|
523
728
|
std::vector<uint8_t> buffer(buffer_size);
|
|
524
729
|
|
|
525
730
|
int bytes_received = recv(socket, reinterpret_cast<char*>(buffer.data()), buffer_size, 0);
|
|
526
|
-
if (bytes_received ==
|
|
731
|
+
if (bytes_received == RATS_SOCKET_ERROR) {
|
|
527
732
|
int error = get_last_socket_error();
|
|
528
733
|
#ifdef _WIN32
|
|
529
734
|
if (error == WSAEWOULDBLOCK) { return {}; }
|
|
@@ -579,7 +784,7 @@ static std::vector<uint8_t> receive_exact_bytes(socket_t socket, size_t num_byte
|
|
|
579
784
|
std::vector<uint8_t> buffer(num_bytes - total_received);
|
|
580
785
|
int bytes_received = recv(socket, reinterpret_cast<char*>(buffer.data()), buffer.size(), 0);
|
|
581
786
|
|
|
582
|
-
if (bytes_received ==
|
|
787
|
+
if (bytes_received == RATS_SOCKET_ERROR) {
|
|
583
788
|
int error = get_last_socket_error();
|
|
584
789
|
#ifdef _WIN32
|
|
585
790
|
if (error == WSAEWOULDBLOCK) {
|
|
@@ -656,7 +861,7 @@ int send_tcp_string(socket_t socket, const std::string& data) {
|
|
|
656
861
|
// ── UDP Socket Functions ────────────────────────────────────────────────────
|
|
657
862
|
|
|
658
863
|
socket_t create_udp_socket(int port, const std::string& bind_address, AddressFamily af) {
|
|
659
|
-
if (!validate_port(port)) return
|
|
864
|
+
if (!validate_port(port)) return RATS_INVALID_SOCKET;
|
|
660
865
|
|
|
661
866
|
const char* af_label = (af == AddressFamily::IPv4) ? "IPv4" :
|
|
662
867
|
(af == AddressFamily::IPv6) ? "IPv6" : "dual stack";
|
|
@@ -666,28 +871,55 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
666
871
|
int family = (af == AddressFamily::IPv4) ? AF_INET : AF_INET6;
|
|
667
872
|
|
|
668
873
|
socket_t udp_socket = socket(family, SOCK_DGRAM, 0);
|
|
669
|
-
if (udp_socket ==
|
|
874
|
+
if (udp_socket == RATS_INVALID_SOCKET) {
|
|
670
875
|
LOG_SOCKET_ERROR("Failed to create " << af_label << " UDP socket (error: "
|
|
671
876
|
<< socket_error_string(get_last_socket_error()) << ")");
|
|
672
|
-
return
|
|
877
|
+
return RATS_INVALID_SOCKET;
|
|
673
878
|
}
|
|
674
879
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
880
|
+
#ifdef _WIN32
|
|
881
|
+
// Disable the Windows-only "UDP connection reset" behaviour. By default, when a
|
|
882
|
+
// datagram we sent provokes an ICMP "port unreachable", Windows fails the *next*
|
|
883
|
+
// recvfrom() on this socket with WSAECONNRESET — nonsensical for a connectionless
|
|
884
|
+
// protocol. A DHT constantly sends to dead/unreachable nodes, so without this every
|
|
885
|
+
// such ICMP would cost us a receive cycle. FALSE turns it off.
|
|
886
|
+
{
|
|
887
|
+
BOOL connreset = FALSE;
|
|
888
|
+
DWORD bytes_returned = 0;
|
|
889
|
+
WSAIoctl(udp_socket, SIO_UDP_CONNRESET, &connreset, sizeof(connreset),
|
|
890
|
+
nullptr, 0, &bytes_returned, nullptr, nullptr);
|
|
891
|
+
}
|
|
892
|
+
#endif
|
|
893
|
+
|
|
894
|
+
// Reuse the address — but only for a port we asked for by number, where the
|
|
895
|
+
// point is to rebind a port a previous run may still be lingering on.
|
|
896
|
+
//
|
|
897
|
+
// Never when the kernel is choosing the port. On a datagram socket the option
|
|
898
|
+
// does not merely relax rebinding, it takes the port out of the set an auto-bind
|
|
899
|
+
// must avoid: asked for port 0, the kernel may return a port another socket
|
|
900
|
+
// already holds, provided that one set the option too — and every UDP socket
|
|
901
|
+
// here did. The two then share the port and the kernel picks one of them per
|
|
902
|
+
// datagram, so a dial-only socket can land on the port a listener beside it is
|
|
903
|
+
// serving and start eating its traffic. Rare, and invisible from either end.
|
|
904
|
+
if (port != 0) {
|
|
905
|
+
int opt = 1;
|
|
906
|
+
if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR,
|
|
907
|
+
(char*)&opt, sizeof(opt)) == RATS_SOCKET_ERROR) {
|
|
908
|
+
LOG_SOCKET_ERROR("Failed to set " << af_label << " UDP socket options (error: "
|
|
909
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
910
|
+
close_socket(udp_socket);
|
|
911
|
+
return RATS_INVALID_SOCKET;
|
|
912
|
+
}
|
|
682
913
|
}
|
|
683
914
|
|
|
684
915
|
// For IPv6/DualStack sockets, configure IPV6_V6ONLY
|
|
685
916
|
if (family == AF_INET6) {
|
|
686
917
|
int ipv6_only = (af == AddressFamily::IPv6) ? 1 : 0;
|
|
687
918
|
if (setsockopt(udp_socket, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
688
|
-
(char*)&ipv6_only, sizeof(ipv6_only)) ==
|
|
919
|
+
(char*)&ipv6_only, sizeof(ipv6_only)) == RATS_SOCKET_ERROR) {
|
|
689
920
|
if (af == AddressFamily::DualStack) {
|
|
690
|
-
LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only"
|
|
921
|
+
LOG_SOCKET_WARN("Failed to disable IPv6-only mode, will be IPv6 only (error: "
|
|
922
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
691
923
|
}
|
|
692
924
|
}
|
|
693
925
|
}
|
|
@@ -705,15 +937,15 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
705
937
|
if (inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr) != 1) {
|
|
706
938
|
LOG_SOCKET_ERROR("Invalid IPv4 bind address: " << bind_address);
|
|
707
939
|
close_socket(udp_socket);
|
|
708
|
-
return
|
|
940
|
+
return RATS_INVALID_SOCKET;
|
|
709
941
|
}
|
|
710
942
|
}
|
|
711
943
|
|
|
712
|
-
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) ==
|
|
944
|
+
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == RATS_SOCKET_ERROR) {
|
|
945
|
+
const int error = get_last_socket_error();
|
|
713
946
|
LOG_SOCKET_ERROR("Failed to bind " << af_label << " UDP socket to port " << port
|
|
714
|
-
<< " (error: " << socket_error_string(
|
|
715
|
-
|
|
716
|
-
return INVALID_SOCKET_VALUE;
|
|
947
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
948
|
+
return fail_socket(udp_socket, error);
|
|
717
949
|
}
|
|
718
950
|
} else {
|
|
719
951
|
sockaddr_in6 addr;
|
|
@@ -727,15 +959,15 @@ socket_t create_udp_socket(int port, const std::string& bind_address, AddressFam
|
|
|
727
959
|
if (inet_pton(AF_INET6, bind_address.c_str(), &addr.sin6_addr) != 1) {
|
|
728
960
|
LOG_SOCKET_ERROR("Invalid IPv6 bind address: " << bind_address);
|
|
729
961
|
close_socket(udp_socket);
|
|
730
|
-
return
|
|
962
|
+
return RATS_INVALID_SOCKET;
|
|
731
963
|
}
|
|
732
964
|
}
|
|
733
965
|
|
|
734
|
-
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) ==
|
|
966
|
+
if (bind(udp_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == RATS_SOCKET_ERROR) {
|
|
967
|
+
const int error = get_last_socket_error();
|
|
735
968
|
LOG_SOCKET_ERROR("Failed to bind " << af_label << " UDP socket to port " << port
|
|
736
|
-
<< " (error: " << socket_error_string(
|
|
737
|
-
|
|
738
|
-
return INVALID_SOCKET_VALUE;
|
|
969
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
970
|
+
return fail_socket(udp_socket, error);
|
|
739
971
|
}
|
|
740
972
|
}
|
|
741
973
|
|
|
@@ -833,7 +1065,7 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data,
|
|
|
833
1065
|
|
|
834
1066
|
int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
|
|
835
1067
|
reinterpret_cast<sockaddr*>(&dest_addr), addr_len);
|
|
836
|
-
if (bytes_sent ==
|
|
1068
|
+
if (bytes_sent == RATS_SOCKET_ERROR) {
|
|
837
1069
|
LOG_SOCKET_ERROR("Failed to send UDP data to " << host << ":" << port
|
|
838
1070
|
<< " (error: " << socket_error_string(get_last_socket_error()) << ")");
|
|
839
1071
|
return -1;
|
|
@@ -843,8 +1075,247 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data,
|
|
|
843
1075
|
return bytes_sent;
|
|
844
1076
|
}
|
|
845
1077
|
|
|
846
|
-
|
|
847
|
-
|
|
1078
|
+
// Build a UDP destination sockaddr straight from a numeric Address — no hostname
|
|
1079
|
+
// resolution and no inet_pton, just a memcpy of the raw address bytes. On a
|
|
1080
|
+
// DualStack/IPv6 socket an IPv4 address is written as an IPv4-mapped IPv6 address.
|
|
1081
|
+
static bool build_udp_dest_addr(const IpAddress& ip, int port, AddressFamily af,
|
|
1082
|
+
sockaddr_storage& addr, socklen_t& addr_len) {
|
|
1083
|
+
memset(&addr, 0, sizeof(addr));
|
|
1084
|
+
if (ip.is_v6()) {
|
|
1085
|
+
auto* a6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
|
1086
|
+
a6->sin6_family = AF_INET6;
|
|
1087
|
+
a6->sin6_port = htons(static_cast<uint16_t>(port));
|
|
1088
|
+
memcpy(&a6->sin6_addr, ip.bytes().data(), 16);
|
|
1089
|
+
addr_len = sizeof(sockaddr_in6);
|
|
1090
|
+
return true;
|
|
1091
|
+
}
|
|
1092
|
+
if (ip.is_v4()) {
|
|
1093
|
+
if (af == AddressFamily::IPv4) {
|
|
1094
|
+
auto* a4 = reinterpret_cast<sockaddr_in*>(&addr);
|
|
1095
|
+
a4->sin_family = AF_INET;
|
|
1096
|
+
a4->sin_port = htons(static_cast<uint16_t>(port));
|
|
1097
|
+
memcpy(&a4->sin_addr, ip.bytes().data(), 4);
|
|
1098
|
+
addr_len = sizeof(sockaddr_in);
|
|
1099
|
+
} else {
|
|
1100
|
+
auto* a6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
|
1101
|
+
a6->sin6_family = AF_INET6;
|
|
1102
|
+
a6->sin6_port = htons(static_cast<uint16_t>(port));
|
|
1103
|
+
a6->sin6_addr.s6_addr[10] = 0xff;
|
|
1104
|
+
a6->sin6_addr.s6_addr[11] = 0xff;
|
|
1105
|
+
memcpy(&a6->sin6_addr.s6_addr[12], ip.bytes().data(), 4);
|
|
1106
|
+
addr_len = sizeof(sockaddr_in6);
|
|
1107
|
+
}
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1110
|
+
return false; // unspecified — nothing to send to
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
int send_udp_data(socket_t socket, const std::vector<uint8_t>& data,
|
|
1114
|
+
const Address& dest, AddressFamily af) {
|
|
1115
|
+
sockaddr_storage dest_addr;
|
|
1116
|
+
socklen_t addr_len;
|
|
1117
|
+
if (!build_udp_dest_addr(dest.ip, dest.port, af, dest_addr, addr_len)) return -1;
|
|
1118
|
+
|
|
1119
|
+
int bytes_sent = sendto(socket, (char*)data.data(), data.size(), 0,
|
|
1120
|
+
reinterpret_cast<sockaddr*>(&dest_addr), addr_len);
|
|
1121
|
+
if (bytes_sent == RATS_SOCKET_ERROR) {
|
|
1122
|
+
LOG_SOCKET_ERROR("Failed to send UDP data to " << dest.to_string()
|
|
1123
|
+
<< " (error: " << socket_error_string(get_last_socket_error()) << ")");
|
|
1124
|
+
return -1;
|
|
1125
|
+
}
|
|
1126
|
+
return bytes_sent;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
bool set_socket_buffer_sizes(socket_t socket, int recv_bytes, int send_bytes) {
|
|
1130
|
+
bool ok = true;
|
|
1131
|
+
if (recv_bytes > 0 &&
|
|
1132
|
+
setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
|
|
1133
|
+
reinterpret_cast<const char*>(&recv_bytes), sizeof(recv_bytes)) != 0) {
|
|
1134
|
+
LOG_SOCKET_DEBUG("Could not set SO_RCVBUF to " << recv_bytes);
|
|
1135
|
+
ok = false;
|
|
1136
|
+
}
|
|
1137
|
+
if (send_bytes > 0 &&
|
|
1138
|
+
setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
|
|
1139
|
+
reinterpret_cast<const char*>(&send_bytes), sizeof(send_bytes)) != 0) {
|
|
1140
|
+
LOG_SOCKET_DEBUG("Could not set SO_SNDBUF to " << send_bytes);
|
|
1141
|
+
ok = false;
|
|
1142
|
+
}
|
|
1143
|
+
return ok;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
bool set_tcp_nodelay(socket_t socket) {
|
|
1147
|
+
const int on = 1;
|
|
1148
|
+
if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
|
|
1149
|
+
reinterpret_cast<const char*>(&on), sizeof(on)) != 0) {
|
|
1150
|
+
LOG_SOCKET_DEBUG("Could not disable Nagle on socket " << socket);
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
std::ptrdiff_t send_udp_to(socket_t socket, const void* data, size_t len,
|
|
1157
|
+
const Address& dest, AddressFamily af) {
|
|
1158
|
+
sockaddr_storage dest_addr;
|
|
1159
|
+
socklen_t addr_len;
|
|
1160
|
+
if (!build_udp_dest_addr(dest.ip, dest.port, af, dest_addr, addr_len)) return -1;
|
|
1161
|
+
|
|
1162
|
+
const int sent = sendto(socket, static_cast<const char*>(data), static_cast<int>(len), 0,
|
|
1163
|
+
reinterpret_cast<sockaddr*>(&dest_addr), addr_len);
|
|
1164
|
+
if (sent != RATS_SOCKET_ERROR) return sent;
|
|
1165
|
+
|
|
1166
|
+
const int error = get_last_socket_error();
|
|
1167
|
+
#ifdef _WIN32
|
|
1168
|
+
if (error == WSAEWOULDBLOCK) return 0;
|
|
1169
|
+
#else
|
|
1170
|
+
if (error == EAGAIN || error == EWOULDBLOCK || error == ENOBUFS) return 0;
|
|
1171
|
+
#endif
|
|
1172
|
+
// A datagram socket reports per-destination problems (ICMP unreachable on a
|
|
1173
|
+
// connected socket, a route that just went away) asynchronously on a later call.
|
|
1174
|
+
// They say nothing about the socket itself, so they are the caller's business,
|
|
1175
|
+
// not a fatal condition — hence a debug log rather than an error.
|
|
1176
|
+
LOG_SOCKET_DEBUG("Failed to send datagram to " << dest.to_string()
|
|
1177
|
+
<< " (error: " << socket_error_string(error) << ")");
|
|
1178
|
+
return -1;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
std::ptrdiff_t recv_udp_from(socket_t socket, void* buffer, size_t len, Address& from) {
|
|
1182
|
+
sockaddr_storage sender_addr;
|
|
1183
|
+
socklen_t sender_addr_len = sizeof(sender_addr);
|
|
1184
|
+
|
|
1185
|
+
const int received = recvfrom(socket, static_cast<char*>(buffer), static_cast<int>(len), 0,
|
|
1186
|
+
reinterpret_cast<sockaddr*>(&sender_addr), &sender_addr_len);
|
|
1187
|
+
if (received != RATS_SOCKET_ERROR) {
|
|
1188
|
+
extract_sender_peer(sender_addr, from);
|
|
1189
|
+
return received;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
const int error = get_last_socket_error();
|
|
1193
|
+
#ifdef _WIN32
|
|
1194
|
+
if (error == WSAEWOULDBLOCK) return kUdpRecvWouldBlock;
|
|
1195
|
+
// Windows reports an ICMP port-unreachable for a *previous* datagram as an error
|
|
1196
|
+
// on the next recvfrom(). It concerns one destination, not the socket, so keep
|
|
1197
|
+
// draining rather than treating the whole mux as broken.
|
|
1198
|
+
if (error == WSAECONNRESET || error == WSAENETRESET) return kUdpRecvError;
|
|
1199
|
+
#else
|
|
1200
|
+
if (error == EAGAIN || error == EWOULDBLOCK) return kUdpRecvWouldBlock;
|
|
1201
|
+
if (error == EINTR || error == ECONNREFUSED) return kUdpRecvError;
|
|
1202
|
+
#endif
|
|
1203
|
+
LOG_SOCKET_DEBUG("Failed to receive datagram: " << socket_error_string(error));
|
|
1204
|
+
return kUdpRecvError;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// ── Batched datagram I/O ────────────────────────────────────────────────────
|
|
1208
|
+
|
|
1209
|
+
std::ptrdiff_t recv_udp_batch(socket_t socket, UdpBatchSlot* slots, size_t count) {
|
|
1210
|
+
if (count == 0) return kUdpRecvWouldBlock;
|
|
1211
|
+
if (count > kUdpBatchMax) count = kUdpBatchMax;
|
|
1212
|
+
|
|
1213
|
+
#ifdef RATS_HAVE_MMSG
|
|
1214
|
+
mmsghdr msgs[kUdpBatchMax];
|
|
1215
|
+
iovec iov[kUdpBatchMax];
|
|
1216
|
+
sockaddr_storage addrs[kUdpBatchMax];
|
|
1217
|
+
|
|
1218
|
+
memset(msgs, 0, sizeof(mmsghdr) * count);
|
|
1219
|
+
for (size_t i = 0; i < count; ++i) {
|
|
1220
|
+
iov[i].iov_base = slots[i].data;
|
|
1221
|
+
iov[i].iov_len = slots[i].len;
|
|
1222
|
+
msgs[i].msg_hdr.msg_name = &addrs[i];
|
|
1223
|
+
msgs[i].msg_hdr.msg_namelen = sizeof(sockaddr_storage);
|
|
1224
|
+
msgs[i].msg_hdr.msg_iov = &iov[i];
|
|
1225
|
+
msgs[i].msg_hdr.msg_iovlen = 1;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// MSG_DONTWAIT matters even on a socket that is already non-blocking: without
|
|
1229
|
+
// it recvmmsg() waits for the *whole* array to fill when no timeout is given.
|
|
1230
|
+
const int n = recvmmsg(socket, msgs, static_cast<unsigned int>(count), MSG_DONTWAIT, nullptr);
|
|
1231
|
+
if (n > 0) {
|
|
1232
|
+
for (int i = 0; i < n; ++i) {
|
|
1233
|
+
slots[i].len = msgs[i].msg_len;
|
|
1234
|
+
extract_sender_peer(addrs[i], slots[i].endpoint);
|
|
1235
|
+
}
|
|
1236
|
+
return n;
|
|
1237
|
+
}
|
|
1238
|
+
if (n == 0) return kUdpRecvWouldBlock;
|
|
1239
|
+
|
|
1240
|
+
const int error = get_last_socket_error();
|
|
1241
|
+
if (error == EAGAIN || error == EWOULDBLOCK) return kUdpRecvWouldBlock;
|
|
1242
|
+
// Only an error on the *first* datagram is reported here; one further in simply
|
|
1243
|
+
// truncates the batch, which is why a short result is not proof of an empty
|
|
1244
|
+
// queue. Either way this concerns one destination, not the socket.
|
|
1245
|
+
if (error == EINTR || error == ECONNREFUSED) return kUdpRecvError;
|
|
1246
|
+
LOG_SOCKET_DEBUG("Failed to receive a datagram batch: " << socket_error_string(error));
|
|
1247
|
+
return kUdpRecvError;
|
|
1248
|
+
#else
|
|
1249
|
+
size_t got = 0;
|
|
1250
|
+
for (size_t i = 0; i < count; ++i) {
|
|
1251
|
+
Address from;
|
|
1252
|
+
const std::ptrdiff_t n = recv_udp_from(socket, slots[i].data, slots[i].len, from);
|
|
1253
|
+
if (n == kUdpRecvWouldBlock) break;
|
|
1254
|
+
// Report what has already arrived and let the caller come back for the rest;
|
|
1255
|
+
// only an error on the very first datagram has nothing to report alongside it.
|
|
1256
|
+
if (n == kUdpRecvError) return got > 0 ? static_cast<std::ptrdiff_t>(got) : kUdpRecvError;
|
|
1257
|
+
slots[i].len = static_cast<size_t>(n);
|
|
1258
|
+
slots[i].endpoint = from;
|
|
1259
|
+
++got;
|
|
1260
|
+
}
|
|
1261
|
+
return got > 0 ? static_cast<std::ptrdiff_t>(got) : kUdpRecvWouldBlock;
|
|
1262
|
+
#endif
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
size_t send_udp_batch(socket_t socket, const UdpBatchSlot* slots, size_t count, AddressFamily af) {
|
|
1266
|
+
if (count == 0) return 0;
|
|
1267
|
+
if (count > kUdpBatchMax) count = kUdpBatchMax;
|
|
1268
|
+
|
|
1269
|
+
#ifdef RATS_HAVE_MMSG
|
|
1270
|
+
mmsghdr msgs[kUdpBatchMax];
|
|
1271
|
+
iovec iov[kUdpBatchMax];
|
|
1272
|
+
sockaddr_storage addrs[kUdpBatchMax];
|
|
1273
|
+
socklen_t addr_lens[kUdpBatchMax];
|
|
1274
|
+
|
|
1275
|
+
memset(msgs, 0, sizeof(mmsghdr) * count);
|
|
1276
|
+
size_t staged = 0;
|
|
1277
|
+
for (size_t i = 0; i < count; ++i) {
|
|
1278
|
+
if (slots[i].len == 0) continue;
|
|
1279
|
+
// An unspecified destination has nowhere to go; skip it rather than letting
|
|
1280
|
+
// one bad slot fail the whole batch.
|
|
1281
|
+
if (!build_udp_dest_addr(slots[i].endpoint.ip, slots[i].endpoint.port, af,
|
|
1282
|
+
addrs[staged], addr_lens[staged]))
|
|
1283
|
+
continue;
|
|
1284
|
+
|
|
1285
|
+
iov[staged].iov_base = const_cast<uint8_t*>(slots[i].data);
|
|
1286
|
+
iov[staged].iov_len = slots[i].len;
|
|
1287
|
+
msgs[staged].msg_hdr.msg_name = &addrs[staged];
|
|
1288
|
+
msgs[staged].msg_hdr.msg_namelen = addr_lens[staged];
|
|
1289
|
+
msgs[staged].msg_hdr.msg_iov = &iov[staged];
|
|
1290
|
+
msgs[staged].msg_hdr.msg_iovlen = 1;
|
|
1291
|
+
++staged;
|
|
1292
|
+
}
|
|
1293
|
+
if (staged == 0) return 0;
|
|
1294
|
+
|
|
1295
|
+
const int n = sendmmsg(socket, msgs, static_cast<unsigned int>(staged),
|
|
1296
|
+
MSG_DONTWAIT | MSG_NOSIGNAL);
|
|
1297
|
+
if (n >= 0) return static_cast<size_t>(n);
|
|
1298
|
+
|
|
1299
|
+
const int error = get_last_socket_error();
|
|
1300
|
+
// A full send buffer drops the batch, exactly as a congested link would drop it.
|
|
1301
|
+
if (error == EAGAIN || error == EWOULDBLOCK || error == ENOBUFS) return 0;
|
|
1302
|
+
LOG_SOCKET_DEBUG("Failed to send a datagram batch (error: "
|
|
1303
|
+
<< socket_error_string(error) << ")");
|
|
1304
|
+
return 0;
|
|
1305
|
+
#else
|
|
1306
|
+
size_t sent = 0;
|
|
1307
|
+
for (size_t i = 0; i < count; ++i) {
|
|
1308
|
+
if (slots[i].len == 0) continue;
|
|
1309
|
+
if (send_udp_to(socket, slots[i].data, slots[i].len, slots[i].endpoint, af) > 0) ++sent;
|
|
1310
|
+
}
|
|
1311
|
+
return sent;
|
|
1312
|
+
#endif
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Address& sender_peer,
|
|
1316
|
+
int timeout_ms, socket_t interrupt_fd, bool* error_out) {
|
|
1317
|
+
if (error_out) *error_out = false;
|
|
1318
|
+
|
|
848
1319
|
// Handle timeout (and optional interrupt socket) using select. When no interrupt
|
|
849
1320
|
// fd is supplied this path is identical to the plain timeout behavior.
|
|
850
1321
|
const bool have_interrupt = is_valid_socket(interrupt_fd);
|
|
@@ -868,19 +1339,29 @@ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer&
|
|
|
868
1339
|
|
|
869
1340
|
int result = select(static_cast<int>(maxfd) + 1, &read_fds, nullptr, nullptr, ptimeout);
|
|
870
1341
|
if (result == 0) {
|
|
871
|
-
|
|
1342
|
+
// Timeout with no data — normal control flow for a polling/idle loop, so it's
|
|
1343
|
+
// deliberately not logged (it would spam every idle cycle, e.g. the DHT runner).
|
|
872
1344
|
return {};
|
|
873
1345
|
} else if (result < 0) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1346
|
+
const int error = get_last_socket_error();
|
|
1347
|
+
#ifndef _WIN32
|
|
1348
|
+
// A signal cut the wait short — nothing is wrong with the socket. select()
|
|
1349
|
+
// is never restarted by SA_RESTART, so a host process with a periodic timer
|
|
1350
|
+
// signal (a sampling profiler, say) can hit this on every poll; reporting it
|
|
1351
|
+
// as an error would let a caller that counts consecutive failures give up on
|
|
1352
|
+
// a perfectly healthy socket. Report "no data" and let it poll again.
|
|
1353
|
+
if (error == EINTR) { return {}; }
|
|
1354
|
+
#endif
|
|
1355
|
+
LOG_SOCKET_ERROR("Select error while waiting for UDP data: " << socket_error_string(error));
|
|
1356
|
+
if (error_out) *error_out = true;
|
|
880
1357
|
return {};
|
|
881
1358
|
}
|
|
882
|
-
//
|
|
1359
|
+
// Prefer real data: if a datagram arrived alongside a wakeup, read it now rather
|
|
1360
|
+
// than deferring it a loop iteration. The wakeup byte stays buffered and is drained
|
|
1361
|
+
// by the caller right after, so no wakeup is lost by checking data first.
|
|
883
1362
|
if (!FD_ISSET(socket, &read_fds)) {
|
|
1363
|
+
// No data — must have been the interrupt socket (e.g. stop/posted work).
|
|
1364
|
+
// Report no data so the caller can re-check its stop flag / run posted tasks.
|
|
884
1365
|
return {};
|
|
885
1366
|
}
|
|
886
1367
|
}
|
|
@@ -892,14 +1373,30 @@ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer&
|
|
|
892
1373
|
int bytes_received = recvfrom(socket, (char*)buffer.data(), buffer_size, 0,
|
|
893
1374
|
reinterpret_cast<sockaddr*>(&sender_addr), &sender_addr_len);
|
|
894
1375
|
|
|
895
|
-
if (bytes_received ==
|
|
1376
|
+
if (bytes_received == RATS_SOCKET_ERROR) {
|
|
896
1377
|
int error = get_last_socket_error();
|
|
897
1378
|
#ifdef _WIN32
|
|
898
1379
|
if (error == WSAEWOULDBLOCK) { return {}; }
|
|
1380
|
+
// Windows reports an ICMP port-unreachable for a *previous* datagram as an error
|
|
1381
|
+
// on the next recvfrom(). It concerns one destination, not the socket, so it is
|
|
1382
|
+
// not a reason to declare the receive path broken. Same classification as
|
|
1383
|
+
// recv_udp_from().
|
|
1384
|
+
if (error == WSAECONNRESET || error == WSAENETRESET) {
|
|
1385
|
+
LOG_SOCKET_DEBUG("Transient UDP receive error: " << socket_error_string(error));
|
|
1386
|
+
return {};
|
|
1387
|
+
}
|
|
899
1388
|
#else
|
|
900
1389
|
if (error == EAGAIN || error == EWOULDBLOCK) { return {}; }
|
|
1390
|
+
// EINTR is a signal, ECONNREFUSED an asynchronous ICMP unreachable for one
|
|
1391
|
+
// destination — neither says anything about the socket itself. Same
|
|
1392
|
+
// classification as recv_udp_from().
|
|
1393
|
+
if (error == EINTR || error == ECONNREFUSED) {
|
|
1394
|
+
LOG_SOCKET_DEBUG("Transient UDP receive error: " << socket_error_string(error));
|
|
1395
|
+
return {};
|
|
1396
|
+
}
|
|
901
1397
|
#endif
|
|
902
1398
|
LOG_SOCKET_DEBUG("Failed to receive UDP data: " << socket_error_string(error));
|
|
1399
|
+
if (error_out) *error_out = true;
|
|
903
1400
|
return {};
|
|
904
1401
|
}
|
|
905
1402
|
|
|
@@ -910,7 +1407,7 @@ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer&
|
|
|
910
1407
|
|
|
911
1408
|
extract_sender_peer(sender_addr, sender_peer);
|
|
912
1409
|
|
|
913
|
-
LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from " << sender_peer.
|
|
1410
|
+
LOG_SOCKET_DEBUG("Received " << bytes_received << " bytes from " << sender_peer.to_string());
|
|
914
1411
|
|
|
915
1412
|
buffer.resize(bytes_received);
|
|
916
1413
|
return buffer;
|
|
@@ -937,30 +1434,37 @@ void close_socket(socket_t socket, bool force) {
|
|
|
937
1434
|
#endif
|
|
938
1435
|
}
|
|
939
1436
|
|
|
940
|
-
|
|
1437
|
+
#ifdef _WIN32
|
|
1438
|
+
::closesocket(socket);
|
|
1439
|
+
#else
|
|
1440
|
+
::close(socket);
|
|
1441
|
+
#endif
|
|
941
1442
|
}
|
|
942
1443
|
}
|
|
943
1444
|
|
|
944
1445
|
bool is_valid_socket(socket_t socket) {
|
|
945
|
-
return socket !=
|
|
1446
|
+
return socket != RATS_INVALID_SOCKET;
|
|
946
1447
|
}
|
|
947
1448
|
|
|
948
1449
|
bool set_socket_nonblocking(socket_t socket) {
|
|
949
1450
|
#ifdef _WIN32
|
|
950
1451
|
unsigned long mode = 1;
|
|
951
1452
|
if (ioctlsocket(socket, FIONBIO, &mode) != 0) {
|
|
952
|
-
LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode"
|
|
1453
|
+
LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode (error: "
|
|
1454
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
953
1455
|
return false;
|
|
954
1456
|
}
|
|
955
1457
|
#else
|
|
956
1458
|
int flags = fcntl(socket, F_GETFL, 0);
|
|
957
1459
|
if (flags == -1) {
|
|
958
|
-
LOG_SOCKET_ERROR("Failed to get socket flags"
|
|
1460
|
+
LOG_SOCKET_ERROR("Failed to get socket flags (error: "
|
|
1461
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
959
1462
|
return false;
|
|
960
1463
|
}
|
|
961
1464
|
|
|
962
1465
|
if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
963
|
-
LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode"
|
|
1466
|
+
LOG_SOCKET_ERROR("Failed to set socket to non-blocking mode (error: "
|
|
1467
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
964
1468
|
return false;
|
|
965
1469
|
}
|
|
966
1470
|
#endif
|
|
@@ -973,18 +1477,21 @@ bool set_socket_blocking(socket_t socket) {
|
|
|
973
1477
|
#ifdef _WIN32
|
|
974
1478
|
unsigned long mode = 0;
|
|
975
1479
|
if (ioctlsocket(socket, FIONBIO, &mode) != 0) {
|
|
976
|
-
LOG_SOCKET_ERROR("Failed to set socket to blocking mode"
|
|
1480
|
+
LOG_SOCKET_ERROR("Failed to set socket to blocking mode (error: "
|
|
1481
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
977
1482
|
return false;
|
|
978
1483
|
}
|
|
979
1484
|
#else
|
|
980
1485
|
int flags = fcntl(socket, F_GETFL, 0);
|
|
981
1486
|
if (flags == -1) {
|
|
982
|
-
LOG_SOCKET_ERROR("Failed to get socket flags"
|
|
1487
|
+
LOG_SOCKET_ERROR("Failed to get socket flags (error: "
|
|
1488
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
983
1489
|
return false;
|
|
984
1490
|
}
|
|
985
1491
|
|
|
986
1492
|
if (fcntl(socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
|
|
987
|
-
LOG_SOCKET_ERROR("Failed to set socket to blocking mode"
|
|
1493
|
+
LOG_SOCKET_ERROR("Failed to set socket to blocking mode (error: "
|
|
1494
|
+
<< socket_error_string(get_last_socket_error()) << ")");
|
|
988
1495
|
return false;
|
|
989
1496
|
}
|
|
990
1497
|
#endif
|