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.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,264 @@
1
+ #include "librats/node/dialer.h"
2
+ #include "librats/util/logger.h"
3
+
4
+ #include <algorithm>
5
+ #include <utility>
6
+
7
+ namespace librats {
8
+
9
+ namespace {
10
+
11
+ /// The PeerTransports bit standing for one transport.
12
+ uint8_t mask_for(TransportKind kind) noexcept {
13
+ return kind == TransportKind::Udp ? PeerTransportUdp : PeerTransportTcp;
14
+ }
15
+
16
+ } // namespace
17
+
18
+ std::vector<TransportKind> Dialer::transport_order(uint8_t known) const {
19
+ const TransportKind first = policy_.preferred;
20
+ const TransportKind second = first == TransportKind::Udp ? TransportKind::Tcp
21
+ : TransportKind::Udp;
22
+ const auto allowed = [this](TransportKind k) {
23
+ return k == TransportKind::Udp ? policy_.allow_udp : policy_.allow_tcp;
24
+ };
25
+
26
+ std::vector<TransportKind> order;
27
+ if (allowed(first)) order.push_back(first);
28
+ // A zero fallback delay means "the preferred transport or nothing" — the
29
+ // second entry is exactly what the fallback timer would bring in.
30
+ if (allowed(second) && policy_.fallback_delay.count() > 0) order.push_back(second);
31
+ // ...unless the preferred one is not available at all, in which case the other
32
+ // is not a fallback, it is the only way out.
33
+ if (order.empty() && allowed(second)) order.push_back(second);
34
+
35
+ // A peer at this address has already told us which wires it accepts. Bring
36
+ // those to the front — stably, so the policy still decides between two the
37
+ // peer accepts equally. Nothing is dropped: the knowledge can be stale, and
38
+ // the cost of acting on a stale entry must stay "one fallback delay", never
39
+ // "this target is undialable". A single-entry order is left alone, which is
40
+ // what keeps a zero fallback delay meaning exactly what it says.
41
+ if (known != PeerTransportNone && order.size() > 1) {
42
+ std::stable_partition(order.begin(), order.end(), [known](TransportKind k) {
43
+ return (known & mask_for(k)) != 0;
44
+ });
45
+ }
46
+ return order;
47
+ }
48
+
49
+ void Dialer::dial(const std::string& host, uint16_t port) {
50
+ auto target = std::make_shared<Target>();
51
+ target->host = host;
52
+ target->port = port;
53
+
54
+ bool started = false;
55
+ bool empty = false;
56
+ {
57
+ std::lock_guard<std::mutex> lock(mutex_);
58
+ if (stopped_) return;
59
+ // Under the lock: the order depends on what identify has taught us about
60
+ // this address, which lives behind the same mutex as the bookkeeping.
61
+ target->pending = transport_order(recall(host, port));
62
+ empty = target->pending.empty();
63
+ if (!empty) started = start_next(target);
64
+ }
65
+
66
+ if (empty) LOG_WARN("dialer", "No transport enabled; cannot dial " << host << ":" << port);
67
+
68
+ // Never with the lock held: a failure handler may well dial again (that is
69
+ // what a reconnection policy does), and this mutex is not recursive.
70
+ if (!started) on_failed_(host, port);
71
+ }
72
+
73
+ // ── What identify taught us about an address ────────────────────────────────
74
+
75
+ std::string Dialer::key_for(const std::string& host, uint16_t port) {
76
+ return host + ":" + std::to_string(port);
77
+ }
78
+
79
+ uint8_t Dialer::recall(const std::string& host, uint16_t port) const {
80
+ const auto it = known_.find(key_for(host, port));
81
+ if (it == known_.end()) return PeerTransportNone;
82
+ if (Clock::now() - it->second.learned_at > kMemoryTtl) return PeerTransportNone;
83
+ return it->second.transports;
84
+ }
85
+
86
+ void Dialer::evict_one(Clock::time_point now) {
87
+ // One pass, and only when the cache is full: drop everything that has expired,
88
+ // and note the oldest survivor in case nothing had. A map capped at
89
+ // kMaxRemembered makes this bounded work on a path that runs once per dial.
90
+ auto oldest = known_.end();
91
+ for (auto it = known_.begin(); it != known_.end();) {
92
+ if (now - it->second.learned_at > kMemoryTtl) {
93
+ it = known_.erase(it);
94
+ continue;
95
+ }
96
+ if (oldest == known_.end() || it->second.learned_at < oldest->second.learned_at)
97
+ oldest = it;
98
+ ++it;
99
+ }
100
+ if (known_.size() >= kMaxRemembered && oldest != known_.end()) known_.erase(oldest);
101
+ }
102
+
103
+ void Dialer::remember(const std::string& host, uint16_t port, uint8_t transports) {
104
+ // 0 is "the peer did not say" (an older node predating the field), not
105
+ // "supports nothing" — storing it would teach us the opposite of the truth.
106
+ if (transports == PeerTransportNone || port == 0 || host.empty()) return;
107
+
108
+ const auto now = Clock::now();
109
+ std::lock_guard<std::mutex> lock(mutex_);
110
+
111
+ std::string key = key_for(host, port);
112
+ const auto it = known_.find(key);
113
+ if (it != known_.end()) {
114
+ it->second.transports = transports;
115
+ it->second.learned_at = now;
116
+ return;
117
+ }
118
+
119
+ if (known_.size() >= kMaxRemembered) evict_one(now);
120
+ known_.emplace(std::move(key), Known{transports, now});
121
+ }
122
+
123
+ uint8_t Dialer::known_transports(const std::string& host, uint16_t port) const {
124
+ std::lock_guard<std::mutex> lock(mutex_);
125
+ return recall(host, port);
126
+ }
127
+
128
+ std::vector<TransportKind> Dialer::planned_order(const std::string& host, uint16_t port) const {
129
+ std::lock_guard<std::mutex> lock(mutex_);
130
+ return transport_order(recall(host, port));
131
+ }
132
+
133
+ bool Dialer::start_next(const std::shared_ptr<Target>& target) {
134
+ // Walk the queue rather than taking only its head: a transport the pool cannot
135
+ // provide (no UDP socket, say) never becomes an attempt, so the next one takes
136
+ // its place at once instead of waiting out a fallback delay for nothing.
137
+ while (!target->pending.empty()) {
138
+ const TransportKind kind = target->pending.front();
139
+ target->pending.erase(target->pending.begin());
140
+
141
+ Reactor& reactor = reactors_.pick(kind);
142
+ const ConnId id = reactor.connect(target->host, target->port, kind);
143
+ if (id == kInvalidConnId) continue;
144
+
145
+ const PeerRoute route{reactor.index(), id};
146
+ target->routes.push_back(route);
147
+ attempts_.emplace(Key{route.reactor, route.conn}, target);
148
+ LOG_DEBUG("dialer", "Dialing " << target->host << ":" << target->port
149
+ << " over " << to_string(kind));
150
+
151
+ if (!target->pending.empty()) arm_fallback(target, reactor);
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+
157
+ void Dialer::arm_fallback(const std::shared_ptr<Target>& target, Reactor& reactor) {
158
+ // The timer holds only a weak reference and re-checks the target when it
159
+ // fires, so there is nothing to cancel when the race is decided early: the
160
+ // target is either gone or marked won, and the timer quietly does nothing.
161
+ std::weak_ptr<Target> weak = target;
162
+ const auto delay = policy_.fallback_delay;
163
+ Reactor* r = &reactor;
164
+
165
+ r->execute([this, r, weak, delay] {
166
+ r->schedule(delay, [this, weak] {
167
+ auto target = weak.lock();
168
+ if (!target) return;
169
+
170
+ bool failed = false;
171
+ std::string host;
172
+ uint16_t port = 0;
173
+ {
174
+ std::lock_guard<std::mutex> lock(mutex_);
175
+ if (stopped_ || target->won || target->pending.empty()) return;
176
+ LOG_DEBUG("dialer", "Racing the fallback transport for "
177
+ << target->host << ":" << target->port);
178
+ if (!start_next(target) && target->routes.empty()) {
179
+ failed = true;
180
+ host = target->host;
181
+ port = target->port;
182
+ }
183
+ }
184
+ if (failed) on_failed_(host, port);
185
+ });
186
+ });
187
+ }
188
+
189
+ void Dialer::on_established(PeerRoute route) {
190
+ std::vector<PeerRoute> superseded;
191
+ {
192
+ std::lock_guard<std::mutex> lock(mutex_);
193
+ auto it = attempts_.find(Key{route.reactor, route.conn});
194
+ if (it == attempts_.end()) return; // inbound, or a dial already settled
195
+
196
+ std::shared_ptr<Target> target = it->second;
197
+ target->won = true;
198
+ target->pending.clear();
199
+
200
+ for (const PeerRoute& other : target->routes) {
201
+ attempts_.erase(Key{other.reactor, other.conn});
202
+ if (other != route) superseded.push_back(other);
203
+ }
204
+ target->routes.clear();
205
+ }
206
+
207
+ // Outside the lock: cancel_dial() posts to a reactor, and on this thread it may
208
+ // run the teardown — and therefore on_closed() — straight away.
209
+ //
210
+ // cancel_dial rather than close: a sibling that has finished its own handshake
211
+ // in the meantime is no longer this race's to end. Ending it anyway would be
212
+ // one end deciding, alone, which of two links to a peer lives — and the other
213
+ // end, which never saw this race, can just as easily decide the other way, so
214
+ // both links die and the peer is lost. Left alone, it reaches the peer table as
215
+ // an ordinary duplicate and is resolved there, the same way at both ends.
216
+ for (const PeerRoute& other : superseded)
217
+ reactors_.by_index(other.reactor).cancel_dial(other.conn, CloseReason::DialSuperseded);
218
+ }
219
+
220
+ void Dialer::on_closed(PeerRoute route) {
221
+ bool failed = false;
222
+ std::string host;
223
+ uint16_t port = 0;
224
+ {
225
+ std::lock_guard<std::mutex> lock(mutex_);
226
+ auto it = attempts_.find(Key{route.reactor, route.conn});
227
+ if (it == attempts_.end()) return;
228
+
229
+ std::shared_ptr<Target> target = it->second;
230
+ attempts_.erase(it);
231
+ target->routes.erase(std::remove(target->routes.begin(), target->routes.end(), route),
232
+ target->routes.end());
233
+ if (stopped_ || target->won) return;
234
+
235
+ // This attempt is out. Bring the next transport in now rather than waiting
236
+ // for the fallback timer: a transport that fails fast should not cost the
237
+ // full delay before the other one is even tried.
238
+ if (!start_next(target) && target->routes.empty()) {
239
+ failed = true;
240
+ host = target->host;
241
+ port = target->port;
242
+ }
243
+ }
244
+ if (failed) on_failed_(host, port);
245
+ }
246
+
247
+ void Dialer::start() {
248
+ std::lock_guard<std::mutex> lock(mutex_);
249
+ stopped_ = false;
250
+ attempts_.clear(); // nothing from a previous run survives a restart
251
+ }
252
+
253
+ void Dialer::stop() {
254
+ std::lock_guard<std::mutex> lock(mutex_);
255
+ stopped_ = true;
256
+ attempts_.clear();
257
+ }
258
+
259
+ size_t Dialer::in_flight() const {
260
+ std::lock_guard<std::mutex> lock(mutex_);
261
+ return attempts_.size();
262
+ }
263
+
264
+ } // namespace librats
@@ -0,0 +1,188 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file dialer.h
5
+ * @brief Picks the transport for an outbound connection, and races the fallback.
6
+ *
7
+ * With two equal transports available, "connect to this address" stops being one
8
+ * action. UDP is the better first choice for a peer-to-peer node — one socket and
9
+ * one NAT mapping for every peer, and a source port that is actually dialable —
10
+ * but some networks block or throttle it, and finding that out by waiting for a
11
+ * dial to time out would be unacceptable. So a dial is a small race, in the
12
+ * spirit of Happy Eyeballs (RFC 8305):
13
+ *
14
+ * t = 0 start the preferred transport
15
+ * t = fallback_delay if it has not established yet, start the other one too
16
+ * first to establish wins; attempts still in flight for this target are cancelled
17
+ * all attempts failed report the dial as failed, exactly once
18
+ *
19
+ * Racing rather than falling back sequentially matters: on a UDP-hostile network
20
+ * the cost is the fallback delay, not a full connect timeout, and on a normal
21
+ * network the second attempt is never made at all.
22
+ *
23
+ * ── Not paying for the same discovery twice ─────────────────────────────────
24
+ * A peer says in its identify message which wires it accepts. Racing is how a
25
+ * dial finds that out when it has nothing to go on; paying the fallback delay
26
+ * *again*, on every later dial to an address that already told us, is pure loss —
27
+ * and a reconnection policy redials the same handful of addresses for the life of
28
+ * the node. So the dialer keeps a small cache of address → transports (fed by
29
+ * Node::handle_identify) and starts with a transport the peer actually has.
30
+ *
31
+ * Two properties make that safe to act on:
32
+ * - It reorders, it never removes. Knowledge can be stale — a peer restarts
33
+ * with a different config — and a stale entry must cost a fallback delay,
34
+ * never a target that cannot be dialed at all. The transport the peer did not
35
+ * claim stays in the queue behind the one it did.
36
+ * - Only addresses the node can vouch for are recorded (see the caller). A
37
+ * peer's self-advertised address list is its unverified word about where
38
+ * anyone lives, so it is not allowed to set the dial order for someone else.
39
+ *
40
+ * Cancelling the loser is the reason Reactor::connect hands back a ConnId
41
+ * synchronously — without it every race would cost a second connection that has to
42
+ * be taken down after the fact.
43
+ *
44
+ * ── What this decides, and what it must not ─────────────────────────────────
45
+ * The race decides which transport this node *dials on*, and nothing more. It is
46
+ * settled by timing, here — and the peer never saw a race at all, only connections
47
+ * arriving. So a verdict reached here is one the other end cannot reproduce: if it
48
+ * also decided which link lives, it could just as well keep the one this end
49
+ * dropped, and then each end has torn down the other's choice and the peer is gone.
50
+ *
51
+ * Hence cancel_dial() rather than close(): an attempt that has already finished its
52
+ * handshake is left alone and handed on as an ordinary duplicate, for PeerTable::add
53
+ * to resolve from what both ends see alike (see peer_table.cpp).
54
+ *
55
+ * Threading: dial() is called from any thread; the attempt callbacks arrive on
56
+ * reactor threads. One mutex guards the (small) bookkeeping; nothing on the data
57
+ * path goes through here.
58
+ */
59
+
60
+ #include "librats/core/types.h"
61
+ #include "librats/peer/peer_table.h" // PeerRoute
62
+ #include "librats/transport/reactor_pool.h"
63
+
64
+ #include <chrono>
65
+ #include <cstdint>
66
+ #include <functional>
67
+ #include <map>
68
+ #include <memory>
69
+ #include <mutex>
70
+ #include <string>
71
+ #include <unordered_map>
72
+ #include <vector>
73
+
74
+ namespace librats {
75
+
76
+ struct DialPolicy {
77
+ TransportKind preferred = TransportKind::Udp;
78
+ bool allow_tcp = true;
79
+ bool allow_udp = true;
80
+ std::chrono::milliseconds fallback_delay{1200};
81
+ };
82
+
83
+ class Dialer {
84
+ public:
85
+ using Clock = std::chrono::steady_clock;
86
+
87
+ /// Called once per target when every attempt has failed. Reactor thread.
88
+ using FailureHandler = std::function<void(const std::string& host, uint16_t port)>;
89
+
90
+ /// Addresses the transports cache holds. A node meets far more addresses than
91
+ /// it will ever redial (DHT and PEX see thousands), so this is a cache with a
92
+ /// ceiling, not a directory: past it the least recently learned entry goes.
93
+ static constexpr size_t kMaxRemembered = 1024;
94
+
95
+ /// How long a learned transports mask is trusted. A peer may restart on a
96
+ /// different config, and an entry that outlives the truth would send every
97
+ /// dial to a wire the peer no longer has — which costs the very fallback delay
98
+ /// this cache exists to avoid.
99
+ static constexpr std::chrono::minutes kMemoryTtl{10};
100
+
101
+ Dialer(ReactorPool& reactors, DialPolicy policy, FailureHandler on_failed)
102
+ : reactors_(reactors), policy_(policy), on_failed_(std::move(on_failed)) {}
103
+
104
+ /// Begin dialing `host:port`. Non-blocking; the outcome surfaces through the
105
+ /// node's peer-connected / dial-failed events.
106
+ void dial(const std::string& host, uint16_t port);
107
+
108
+ // — attempt outcomes, reported by the Node (reactor thread) —
109
+
110
+ /// An attempt completed its handshake; it wins the target.
111
+ void on_established(PeerRoute route);
112
+ /// An attempt ended (failed, was superseded, or simply closed later).
113
+ void on_closed(PeerRoute route);
114
+
115
+ /// Record which transports the peer reachable at `host:port` said it accepts
116
+ /// (a PeerTransports bitmask from its identify message). A later dial to the
117
+ /// same address starts with one of them instead of spending the fallback delay
118
+ /// rediscovering it. A mask of 0 ("did not say") is ignored rather than stored
119
+ /// as "supports nothing". Thread-safe; called from a reactor thread.
120
+ void remember(const std::string& host, uint16_t port, uint8_t transports);
121
+
122
+ /// What is currently believed about `host:port`, 0 if nothing (or if what was
123
+ /// learned has expired). Diagnostics and tests.
124
+ uint8_t known_transports(const std::string& host, uint16_t port) const;
125
+
126
+ /// The transports a dial to `host:port` would try, in order — exactly what
127
+ /// dial() computes, without starting anything. Diagnostics and tests.
128
+ std::vector<TransportKind> planned_order(const std::string& host, uint16_t port) const;
129
+
130
+ /// Accept dials again after a stop() — a Node may be restarted. What identify
131
+ /// taught us about addresses is knowledge about the network rather than
132
+ /// in-flight bookkeeping, so it deliberately survives the cycle.
133
+ void start();
134
+
135
+ /// Drop all bookkeeping; no further dials or fallbacks are started.
136
+ void stop();
137
+
138
+ /// Attempts currently in flight (diagnostics/tests).
139
+ size_t in_flight() const;
140
+
141
+ private:
142
+ struct Target {
143
+ std::string host;
144
+ uint16_t port = 0;
145
+ std::vector<TransportKind> pending; ///< transports not yet tried, in order
146
+ std::vector<PeerRoute> routes; ///< attempts currently in flight
147
+ bool won = false;
148
+ };
149
+
150
+ using Key = std::pair<uint8_t, ConnId>; ///< (reactor, conn) — unique per attempt
151
+
152
+ /// What one address last told us about itself.
153
+ struct Known {
154
+ uint8_t transports = 0;
155
+ Clock::time_point learned_at{};
156
+ };
157
+
158
+ /// Start the next available transport in `target`'s queue; false if the queue
159
+ /// is exhausted (nothing was started). Caller holds the mutex — and must
160
+ /// report a resulting failure only after releasing it.
161
+ bool start_next(const std::shared_ptr<Target>& target);
162
+ /// Arm the timer that brings the second transport in. Caller holds the mutex.
163
+ void arm_fallback(const std::shared_ptr<Target>& target, Reactor& reactor);
164
+
165
+ /// The transports to try, in order: what the policy allows, with anything the
166
+ /// peer at this address said it accepts (`known`, 0 for "no idea") moved to
167
+ /// the front. Reorders only — see the note at the top of this file.
168
+ std::vector<TransportKind> transport_order(uint8_t known) const;
169
+
170
+ /// What is known about `host:port`, 0 if nothing or if it has expired. Caller
171
+ /// holds the mutex.
172
+ uint8_t recall(const std::string& host, uint16_t port) const;
173
+ /// Make room for one entry in a full cache. Caller holds the mutex.
174
+ void evict_one(Clock::time_point now);
175
+
176
+ static std::string key_for(const std::string& host, uint16_t port);
177
+
178
+ ReactorPool& reactors_;
179
+ DialPolicy policy_;
180
+ FailureHandler on_failed_;
181
+
182
+ mutable std::mutex mutex_;
183
+ std::map<Key, std::shared_ptr<Target>> attempts_;
184
+ std::unordered_map<std::string, Known> known_; ///< address → transports it accepts
185
+ bool stopped_ = false;
186
+ };
187
+
188
+ } // namespace librats
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file host_events.h
5
+ * @brief Node-level event types published on the EventBus.
6
+ *
7
+ * These describe things that happen to the *host* the node runs on (as opposed to
8
+ * the peer mesh), which several subsystems may want to react to independently.
9
+ * They are plain value types; a subsystem subscribes via `ctx.events.on<...>()`.
10
+ */
11
+
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ namespace librats {
16
+
17
+ /// The set of local interface addresses changed — an interface came up/down, an
18
+ /// IP was added/removed, the default route flipped (Wi-Fi↔cellular, VPN, dock,
19
+ /// wake-from-sleep). Long-lived nodes react by renewing port mappings and
20
+ /// re-discovering/re-announcing their public endpoint, which would otherwise go
21
+ /// stale. Emitted (debounced) by the Node's NetworkMonitor.
22
+ struct NetworkChanged {
23
+ std::vector<std::string> local_addresses; ///< new, full list of local IPs
24
+ };
25
+
26
+ } // namespace librats
@@ -0,0 +1,130 @@
1
+ #include "librats/node/identify.h"
2
+
3
+ #include <utility>
4
+
5
+ namespace librats {
6
+
7
+ namespace {
8
+
9
+ void put_u16(Bytes& out, uint16_t v) {
10
+ out.push_back(static_cast<uint8_t>(v >> 8));
11
+ out.push_back(static_cast<uint8_t>(v & 0xFF));
12
+ }
13
+
14
+ void put_addr(Bytes& out, const Address& a) {
15
+ // [len:u8][ip bytes: 4 or 16][port:u16] — the IP goes out as raw address bytes,
16
+ // not text, so an endpoint costs 7 (v4) or 19 (v6) bytes on the wire.
17
+ const ByteView ip = a.ip.bytes();
18
+ out.push_back(static_cast<uint8_t>(ip.size()));
19
+ out.insert(out.end(), ip.begin(), ip.end());
20
+ put_u16(out, a.port);
21
+ }
22
+
23
+ bool emittable(const Address& a) {
24
+ return !a.ip.is_unspecified(); // size() is then 4 or 16, always within kMaxIpLength
25
+ }
26
+
27
+ /// Bounds-checked forward reader: every read is validated against the remaining
28
+ /// length, and the first short read latches `ok` false so the rest are no-ops.
29
+ struct Reader {
30
+ const uint8_t* p;
31
+ size_t n;
32
+ bool ok = true;
33
+
34
+ explicit Reader(ByteView v) : p(v.data()), n(v.size()) {}
35
+
36
+ uint8_t u8() {
37
+ if (n < 1) { ok = false; return 0; }
38
+ const uint8_t v = *p;
39
+ ++p; --n;
40
+ return v;
41
+ }
42
+ uint16_t u16() {
43
+ const uint8_t hi = u8();
44
+ const uint8_t lo = u8();
45
+ return static_cast<uint16_t>((hi << 8) | lo);
46
+ }
47
+ std::string str(size_t len) {
48
+ if (n < len) { ok = false; return {}; }
49
+ std::string s(reinterpret_cast<const char*>(p), len);
50
+ p += len; n -= len;
51
+ return s;
52
+ }
53
+ };
54
+
55
+ } // namespace
56
+
57
+ Bytes IdentifyMessage::encode() const {
58
+ Bytes out;
59
+ out.push_back(kVersion);
60
+ put_u16(out, listen_port);
61
+
62
+ // Emit at most kMaxAddresses well-formed addresses; the count prefix matches.
63
+ Bytes addr_blob;
64
+ uint8_t emitted = 0;
65
+ for (const Address& a : addresses) {
66
+ if (emitted >= kMaxAddresses) break;
67
+ if (!emittable(a) || a.port == 0) continue;
68
+ put_addr(addr_blob, a);
69
+ ++emitted;
70
+ }
71
+ out.push_back(emitted);
72
+ out.insert(out.end(), addr_blob.begin(), addr_blob.end());
73
+
74
+ // Observed address: an IP length of 0 means "none".
75
+ if (observed && emittable(*observed)) {
76
+ put_addr(out, *observed);
77
+ } else {
78
+ out.push_back(0);
79
+ }
80
+
81
+ // Appended last, where a decoder that predates it just ignores it.
82
+ out.push_back(transports);
83
+ return out;
84
+ }
85
+
86
+ std::optional<IdentifyMessage> IdentifyMessage::decode(ByteView in) {
87
+ Reader r(in);
88
+
89
+ const uint8_t version = r.u8();
90
+ if (!r.ok || version != kVersion) return std::nullopt; // unknown/old → "no identify"
91
+
92
+ IdentifyMessage msg;
93
+ msg.listen_port = r.u16();
94
+
95
+ const uint8_t count = r.u8();
96
+ if (!r.ok || count > kMaxAddresses) return std::nullopt; // capped: never over-reserve
97
+ msg.addresses.reserve(count);
98
+ for (uint8_t i = 0; i < count; ++i) {
99
+ const uint8_t ip_len = r.u8();
100
+ if (!r.ok || (ip_len != 4 && ip_len != 16)) return std::nullopt;
101
+ std::string ip = r.str(ip_len);
102
+ const uint16_t port = r.u16();
103
+ if (!r.ok) return std::nullopt;
104
+ auto addr = IpAddress::from_bytes(ByteView(ip));
105
+ if (!addr) return std::nullopt;
106
+ msg.addresses.push_back(Address{*addr, port});
107
+ }
108
+
109
+ const uint8_t obs_len = r.u8();
110
+ if (!r.ok) return std::nullopt;
111
+ if (obs_len != 0) {
112
+ if (obs_len != 4 && obs_len != 16) return std::nullopt;
113
+ std::string ip = r.str(obs_len);
114
+ const uint16_t port = r.u16();
115
+ if (!r.ok) return std::nullopt;
116
+ auto addr = IpAddress::from_bytes(ByteView(ip));
117
+ if (!addr) return std::nullopt;
118
+ msg.observed = Address{*addr, port};
119
+ }
120
+
121
+ // Optional trailing extension. A sender that predates it leaves the field at
122
+ // 0, which reads as "did not say" rather than "supports nothing".
123
+ const uint8_t transports = r.u8();
124
+ if (r.ok) msg.transports = transports;
125
+
126
+ // Any further trailing bytes (a future extension) are tolerated and ignored.
127
+ return msg;
128
+ }
129
+
130
+ } // namespace librats
@@ -0,0 +1,71 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file identify.h
5
+ * @brief The node's "identify" control message — how peers learn each other's
6
+ * dialable addresses (libp2p-identify in spirit).
7
+ *
8
+ * A TCP socket only reveals a peer's *source* endpoint: its IP plus an ephemeral
9
+ * port the OS picked for the outbound socket — never the port it listens on. So
10
+ * the dialable address of an INBOUND peer is unknowable from the connection
11
+ * alone; the peer must tell us. Right after the secure handshake, each side sends
12
+ * an Identify message over the authenticated channel carrying:
13
+ *
14
+ * - listen_port : the TCP port this node accepts connections on. The receiver
15
+ * pairs it with the IP it sees the sender at to form a dialable
16
+ * address (the key that makes inbound peers reconnectable).
17
+ * - addresses : additional self-advertised dialable addresses (e.g. each local
18
+ * interface IP with the listen port) — the "multiaddr set".
19
+ * - observed : the address the sender observed the RECIPIENT connecting from,
20
+ * so a node can learn its own public IP as peers see it.
21
+ * - transports : which wires the sender accepts on that port (TCP, the
22
+ * datagram transport, or both). Both bind the SAME port, so
23
+ * this is a property of the node rather than of an address —
24
+ * it tells a peer which transports are worth dialing before it
25
+ * spends a fallback delay finding out.
26
+ *
27
+ * The transports byte is appended after the older fields, and decode has always
28
+ * ignored trailing bytes, so a node that predates it simply reads the message
29
+ * without it and reports "unspecified" rather than failing.
30
+ *
31
+ * The wire form is a compact, versioned, fully bounds-checked binary blob — decode
32
+ * never trusts a length without checking it against the buffer, and every count is
33
+ * capped, so a malformed or hostile payload yields nullopt rather than misbehaving.
34
+ */
35
+
36
+ #include "librats/util/rats_export.h"
37
+ #include "librats/core/address.h"
38
+ #include "librats/core/bytes.h"
39
+
40
+ #include <cstdint>
41
+ #include <optional>
42
+ #include <vector>
43
+
44
+ namespace librats {
45
+
46
+ struct RATS_API IdentifyMessage {
47
+ static constexpr uint8_t kVersion = 1; ///< IPs on the wire are raw bytes (4/16), not text
48
+ static constexpr size_t kMaxAddresses = 32; ///< cap advertised addresses
49
+ static constexpr size_t kMaxIpLength = 16; ///< a single IP is 4 (v4) or 16 (v6) bytes
50
+
51
+ /// Transport bitmask values, matching PeerTransports in peer/peer_info.h.
52
+ static constexpr uint8_t kTransportTcp = 1 << 0;
53
+ static constexpr uint8_t kTransportUdp = 1 << 1;
54
+
55
+ uint16_t listen_port = 0;
56
+ std::vector<Address> addresses; ///< sender's self-advertised dialable addrs
57
+ std::optional<Address> observed; ///< address sender saw the recipient at
58
+ /// Transports the sender accepts on listen_port. 0 means the sender did not
59
+ /// say (an older peer); treat that as "no better information", not as "none".
60
+ uint8_t transports = 0;
61
+
62
+ /// Serialise to the wire form. Addresses with an empty IP, an over-long IP, or
63
+ /// a zero port are skipped; at most kMaxAddresses are emitted.
64
+ Bytes encode() const;
65
+
66
+ /// Parse the wire form. Returns nullopt on an unknown version or any
67
+ /// truncation/inconsistency — the caller treats that as "no identify".
68
+ static std::optional<IdentifyMessage> decode(ByteView in);
69
+ };
70
+
71
+ } // namespace librats