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,241 @@
1
+ #include "librats/subsystems/peer_exchange.h"
2
+ #include "librats/node/node_context.h"
3
+ #include "librats/subsystems/hole_punch_service.h"
4
+ #include "librats/util/logger.h"
5
+ #include "librats/util/network_utils.h"
6
+
7
+ #include <algorithm>
8
+ #include <unordered_set>
9
+
10
+ namespace librats {
11
+
12
+ namespace {
13
+
14
+ constexpr uint8_t kVersion = 1; // peer IPs are raw address bytes (4/16), not text
15
+ constexpr uint8_t kRequest = 0;
16
+ constexpr uint8_t kResponse = 1;
17
+
18
+ constexpr size_t kRequestSize = 4; // ver + op + u16 max
19
+
20
+ void put_u16(Bytes& out, uint16_t v) {
21
+ out.push_back(static_cast<uint8_t>(v >> 8));
22
+ out.push_back(static_cast<uint8_t>(v & 0xFF));
23
+ }
24
+
25
+ uint16_t get_u16(const uint8_t* p) {
26
+ return static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | p[1]);
27
+ }
28
+
29
+ } // namespace
30
+
31
+ PeerExchange::PeerExchange() : PeerExchange(Config()) {}
32
+ PeerExchange::PeerExchange(Config config) : config_(std::move(config)) {}
33
+ PeerExchange::~PeerExchange() { stop(); }
34
+
35
+ void PeerExchange::attach(NodeContext& ctx) {
36
+ network_ = &ctx.network;
37
+ services_ = &ctx.services; // both references outlive every subsystem
38
+ network_->on(MessageType::Pex,
39
+ [this](const Peer& peer, ByteView payload) { handle(peer, payload); });
40
+ network_->on_peer_connected([this](const Peer& peer) { on_connected(peer); });
41
+ network_->on_dial_failed([this](const Address& addr) { on_dial_failed(addr); });
42
+ }
43
+
44
+ void PeerExchange::start() {
45
+ // Resolved here rather than in attach(): HolePunch may be attached after us, and
46
+ // every attach() runs before any start(). Stored before running_ goes true, so a
47
+ // dial failure arriving on a reactor thread never sees a half-initialised state.
48
+ punch_.store(config_.punch_on_dial_failure && services_
49
+ ? services_->get<HolePunchService>()
50
+ : nullptr);
51
+ running_.store(true);
52
+ }
53
+
54
+ void PeerExchange::stop() {
55
+ running_.store(false);
56
+ punch_.store(nullptr);
57
+ std::lock_guard<std::mutex> lock(mutex_);
58
+ pending_dials_.clear();
59
+ }
60
+
61
+ // ── Outgoing request (on connect) ────────────────────────────────────────────
62
+
63
+ void PeerExchange::on_connected(const Peer& peer) {
64
+ if (!running_.load() || !config_.request_on_connect) return;
65
+ const uint16_t max = static_cast<uint16_t>(std::min<size_t>(config_.request_max, 0xFFFF));
66
+
67
+ uint8_t req[kRequestSize] = {kVersion, kRequest,
68
+ static_cast<uint8_t>(max >> 8), static_cast<uint8_t>(max & 0xFF)};
69
+ network_->send(peer.id(), MessageType::Pex, ByteView(req, kRequestSize));
70
+ }
71
+
72
+ // ── Inbound dispatch ─────────────────────────────────────────────────────────
73
+
74
+ void PeerExchange::handle(const Peer& peer, ByteView payload) {
75
+ if (!running_.load() || payload.size() < 2) return;
76
+ if (payload.data()[0] != kVersion) return; // unknown version → ignore
77
+
78
+ const uint8_t op = payload.data()[1];
79
+ const ByteView body(payload.data() + 2, payload.size() - 2);
80
+ if (op == kRequest) {
81
+ if (body.size() < 2) return;
82
+ handle_request(peer, get_u16(body.data()));
83
+ } else if (op == kResponse) {
84
+ handle_response(body);
85
+ }
86
+ }
87
+
88
+ void PeerExchange::handle_request(const Peer& requester, uint16_t max) {
89
+ const size_t limit = std::min<size_t>(max, config_.max_addresses_per_response);
90
+ if (limit == 0) return;
91
+ const PeerId& self = network_->local_id();
92
+
93
+ Bytes out;
94
+ out.push_back(kVersion);
95
+ out.push_back(kResponse);
96
+ const size_t count_pos = out.size();
97
+ put_u16(out, 0); // count placeholder, back-patched below
98
+
99
+ uint16_t count = 0;
100
+ for (const PeerInfo& p : network_->peers()) {
101
+ if (count >= limit) break;
102
+ if (p.id == requester.id() || p.id == self) continue; // not the asker, not us
103
+ const Address* addr = pick_shareable(p.addresses);
104
+ if (!addr) continue;
105
+
106
+ const ByteView ip = addr->ip.bytes(); // 4 (v4) or 16 (v6) raw bytes
107
+ out.push_back(static_cast<uint8_t>(ip.size()));
108
+ out.insert(out.end(), ip.begin(), ip.end());
109
+ put_u16(out, addr->port);
110
+ const auto& id_bytes = p.id.bytes();
111
+ out.insert(out.end(), id_bytes.begin(), id_bytes.end());
112
+ ++count;
113
+ }
114
+ if (count == 0) return; // nothing worth sending
115
+
116
+ out[count_pos] = static_cast<uint8_t>(count >> 8);
117
+ out[count_pos + 1] = static_cast<uint8_t>(count & 0xFF);
118
+ network_->send(requester.id(), MessageType::Pex, ByteView(out));
119
+ }
120
+
121
+ void PeerExchange::handle_response(ByteView body) {
122
+ const uint8_t* p = body.data();
123
+ size_t n = body.size();
124
+ // Bounds-checked forward reader: any short read aborts the whole response.
125
+ auto take = [&](size_t k) -> const uint8_t* {
126
+ if (n < k) return nullptr;
127
+ const uint8_t* at = p;
128
+ p += k; n -= k;
129
+ return at;
130
+ };
131
+
132
+ const uint8_t* hdr = take(2);
133
+ if (!hdr) return;
134
+ const uint16_t count = get_u16(hdr);
135
+
136
+ const PeerId self = network_->local_id();
137
+ std::unordered_set<PeerId, PeerId::Hash> connected;
138
+ for (const PeerId& id : network_->connected_peers()) connected.insert(id);
139
+
140
+ // Already at the size the application asked for: read the response no further.
141
+ // The sample is a snapshot anyway — when a peer drops, the next connect asks again.
142
+ if (config_.peer_target && connected.size() >= config_.peer_target) return;
143
+
144
+ size_t dialed = 0;
145
+ for (uint16_t i = 0; i < count; ++i) {
146
+ if (dialed >= config_.max_addresses_per_response) break; // bound receiver work
147
+
148
+ const uint8_t* len_p = take(1);
149
+ if (!len_p) return;
150
+ const uint8_t ip_len = *len_p;
151
+ if (ip_len != 4 && ip_len != 16) return; // malformed
152
+ const uint8_t* ip_p = take(ip_len);
153
+ if (!ip_p) return;
154
+ const auto ip = IpAddress::from_bytes(ByteView(ip_p, ip_len));
155
+ const uint8_t* port_p = take(2);
156
+ if (!port_p) return;
157
+ const uint16_t port = get_u16(port_p);
158
+ const uint8_t* id_p = take(PeerId::kSize);
159
+ if (!id_p) return;
160
+ const auto id = PeerId::from_bytes(ByteView(id_p, PeerId::kSize));
161
+
162
+ if (!id || *id == self || connected.count(*id)) continue; // us / already linked
163
+ if (!ip || port == 0 || ip->is_any()) continue; // not dialable
164
+ Address addr{*ip, port};
165
+ if (!should_dial(addr)) continue; // cooldown / dedup
166
+
167
+ LOG_DEBUG("pex", "Discovered peer " << id->short_hex() << " at " << addr.to_string() << "; dialing");
168
+ remember_dial(addr, *id); // before connect(): the verdict is asynchronous
169
+ network_->connect(addr);
170
+ ++dialed;
171
+ }
172
+ }
173
+
174
+ // ── Punch fallback ───────────────────────────────────────────────────────────
175
+
176
+ void PeerExchange::on_dial_failed(const Address& address) {
177
+ if (!running_.load()) return;
178
+ HolePunchService* punch = punch_.load();
179
+ if (!punch) return; // no HolePunch attached, or the fallback is off
180
+
181
+ PeerId target;
182
+ {
183
+ std::lock_guard<std::mutex> lock(mutex_);
184
+ auto it = pending_dials_.find(address);
185
+ // Not one of ours (ReconnectionService and the app dial too), or too old to
186
+ // still be attributable to a peer id.
187
+ if (it == pending_dials_.end()) return;
188
+ const auto age = std::chrono::steady_clock::now() - it->second.started;
189
+ target = it->second.id;
190
+ pending_dials_.erase(it);
191
+ if (age > config_.punch_window) return;
192
+ }
193
+
194
+ LOG_DEBUG("pex", "Peer " << target.short_hex() << " would not dial at "
195
+ << address.to_string() << "; asking for a hole punch");
196
+ punch->punch(target);
197
+ }
198
+
199
+ void PeerExchange::remember_dial(const Address& addr, const PeerId& id) {
200
+ if (!config_.punch_on_dial_failure) return;
201
+ const auto now = std::chrono::steady_clock::now();
202
+ std::lock_guard<std::mutex> lock(mutex_);
203
+
204
+ // A dial that neither failed nor was reported leaves its entry behind, so the
205
+ // window is also what bounds the map in steady state; the cap is the backstop.
206
+ for (auto it = pending_dials_.begin(); it != pending_dials_.end();) {
207
+ if (now - it->second.started > config_.punch_window) it = pending_dials_.erase(it);
208
+ else ++it;
209
+ }
210
+ if (pending_dials_.size() >= config_.max_pending_dials) return; // best-effort
211
+
212
+ pending_dials_[addr] = PendingDial{id, now};
213
+ }
214
+
215
+ // ── Helpers ──────────────────────────────────────────────────────────────────
216
+
217
+ const Address* PeerExchange::pick_shareable(const std::vector<Address>& addrs) const {
218
+ for (const Address& a : addrs) {
219
+ if (a.port == 0 || a.ip.is_any()) continue;
220
+ if (config_.public_only && !network_utils::is_public_ip(a.ip)) continue;
221
+ return &a;
222
+ }
223
+ return nullptr;
224
+ }
225
+
226
+ bool PeerExchange::should_dial(const Address& addr) {
227
+ const auto now = std::chrono::steady_clock::now();
228
+ std::lock_guard<std::mutex> lock(mutex_);
229
+
230
+ // Drop expired entries so the set stays bounded and old failures can be retried.
231
+ for (auto it = recent_dials_.begin(); it != recent_dials_.end();) {
232
+ if (now - it->second > config_.dial_cooldown) it = recent_dials_.erase(it);
233
+ else ++it;
234
+ }
235
+ if (recent_dials_.size() >= config_.max_recent_dials) return false; // overloaded → back off
236
+
237
+ auto [it, inserted] = recent_dials_.emplace(addr, now);
238
+ return inserted; // false ⇒ dialed within the cooldown window
239
+ }
240
+
241
+ } // namespace librats
@@ -0,0 +1,136 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file peer_exchange.h
5
+ * @brief Peer exchange (PEX): peers gossip the addresses of peers they know, so a
6
+ * node bootstraps the mesh from its existing links — no DHT/tracker needed.
7
+ *
8
+ * A Subsystem built on PeerNetwork, plus one optional capability: when a peer it
9
+ * discovered refuses to be dialed and HolePunch is present, it asks for a NAT hole
10
+ * punch to that peer's id instead of writing the peer off (see below). This first
11
+ * cut is deliberately
12
+ * **pull-only**: when we connect to a peer we ask it for some of its known peers,
13
+ * and it replies with a random sample (address + id). We then dial the ones we do
14
+ * not already have. Both sides must run PeerExchange — the responder needs it to
15
+ * answer the request.
16
+ *
17
+ * It rides on the node's identify layer: identify is what fills in each peer's
18
+ * dialable address (an inbound peer's listen port is otherwise unknown), and PEX
19
+ * simply forwards those addresses on. A discovered peer we dial becomes an
20
+ * outbound link, so — paired with ReconnectionService — it is then persisted and
21
+ * kept alive automatically.
22
+ *
23
+ * Wire format (MessageType::Pex payload), all integers big-endian:
24
+ * Request : [u8 ver=1][u8 op=0][u16 max]
25
+ * Response : [u8 ver=1][u8 op=1][u16 count] × { [u8 ip_len][ip][u16 port][32B peer_id] }
26
+ *
27
+ * ── Punch fallback ──────────────────────────────────────────────────────────
28
+ * A PEX entry carries an id *and* an address, which is exactly what a peer behind a
29
+ * NAT cannot be reached at: its advertised endpoint is unreachable from the outside,
30
+ * so the dial fails and the mesh silently stays one link short. That is the case
31
+ * HolePunch exists for, and PEX is the only module that holds both halves it needs —
32
+ * so each dial started here is remembered by address for `punch_window`, and if
33
+ * on_dial_failed reports that address back, the id is handed to HolePunchService.
34
+ * Entirely optional: with no HolePunch attached the lookup answers nullptr and the
35
+ * failure is simply the end of the story, as before.
36
+ *
37
+ * Safety: response size is capped both sides; the receiver bounds how many it
38
+ * dials per response and de-duplicates dials with a TTL'd cooldown set (so a slow
39
+ * dial or a repeated response can't trigger a connect storm); malformed payloads
40
+ * are ignored, never fatal. `public_only` restricts sharing to globally-routable
41
+ * addresses for WAN deployments that must not relay private/LAN endpoints. The
42
+ * punch fallback inherits those bounds — one punch per dial we ourselves started,
43
+ * and HolePunch caps sessions and cooldowns on top of that.
44
+ *
45
+ * Threading: handlers run on reactor threads (possibly several with a multi-reactor
46
+ * pool), so the cooldown set is mutex-guarded. The subsystem owns no thread.
47
+ */
48
+
49
+ #include "librats/util/rats_export.h"
50
+ #include "librats/node/peer_network.h"
51
+ #include "librats/peer/peer.h"
52
+ #include "librats/peer/peer_id.h"
53
+ #include "librats/core/address.h"
54
+
55
+ #include <atomic>
56
+ #include <chrono>
57
+ #include <cstdint>
58
+ #include <mutex>
59
+ #include <string>
60
+ #include <unordered_map>
61
+ #include <vector>
62
+
63
+ namespace librats {
64
+
65
+ class ServiceRegistry;
66
+ struct HolePunchService;
67
+
68
+ class RATS_API PeerExchange final : public Subsystem {
69
+ public:
70
+ struct Config {
71
+ size_t max_addresses_per_response = 32; ///< cap entries we send / act on per response
72
+ size_t request_max = 32; ///< how many peers we ask for on connect
73
+ bool request_on_connect = true;
74
+ bool public_only = false; ///< only share globally-routable addresses
75
+ std::chrono::milliseconds dial_cooldown{std::chrono::minutes(5)}; ///< re-dial suppression
76
+ size_t max_recent_dials = 4096; ///< bound the cooldown set
77
+
78
+ /// Stop dialing discovered peers once this many are connected. 0 (default)
79
+ /// keeps the historical behaviour: grow without bound. Worth setting,
80
+ /// because NodeConfig::max_peers guards *inbound* connections only — every
81
+ /// discovered peer we dial is honored regardless of it, and PEX is the one
82
+ /// discovery source that compounds (each new peer is asked for more peers).
83
+ /// Answering other peers' requests is unaffected: that costs one message.
84
+ size_t peer_target = 0;
85
+
86
+ /// Ask HolePunch to reach a discovered peer whose address would not dial.
87
+ /// A no-op when no HolePunch is attached.
88
+ bool punch_on_dial_failure = true;
89
+ /// How long a dial started here stays eligible for that fallback. Only has
90
+ /// to outlive the dial itself (connect + handshake, ~15 s worst case).
91
+ std::chrono::milliseconds punch_window{std::chrono::seconds(30)};
92
+ /// Bound on dials awaiting their verdict — one entry is an address and an id.
93
+ size_t max_pending_dials = 512;
94
+ };
95
+
96
+ PeerExchange();
97
+ explicit PeerExchange(Config config);
98
+ ~PeerExchange() override;
99
+
100
+ void attach(NodeContext& ctx) override;
101
+ void start() override;
102
+ void stop() override;
103
+
104
+ private:
105
+ void on_connected(const Peer& peer);
106
+ void handle(const Peer& peer, ByteView payload);
107
+ void handle_request(const Peer& requester, uint16_t max);
108
+ void handle_response(ByteView body);
109
+ /// An outbound dial resolved as failed. Ours ⇒ punch the peer it was meant for.
110
+ void on_dial_failed(const Address& address);
111
+
112
+ const Address* pick_shareable(const std::vector<Address>& addrs) const;
113
+ bool should_dial(const Address& addr); ///< cooldown + dedup; records on success
114
+ /// Note who a dial we are about to start was aimed at. Called BEFORE connect():
115
+ /// the failure lands on a reactor thread and may beat us back here otherwise.
116
+ void remember_dial(const Address& addr, const PeerId& id);
117
+
118
+ Config config_;
119
+ PeerNetwork* network_ = nullptr;
120
+ ServiceRegistry* services_ = nullptr;
121
+ /// Resolved in start(), cleared in stop(). Atomic because a dial failure can
122
+ /// land on a reactor thread the moment the node is up.
123
+ std::atomic<HolePunchService*> punch_{nullptr};
124
+ std::atomic<bool> running_{false};
125
+
126
+ struct PendingDial {
127
+ PeerId id;
128
+ std::chrono::steady_clock::time_point started;
129
+ };
130
+
131
+ std::mutex mutex_;
132
+ std::unordered_map<Address, std::chrono::steady_clock::time_point> recent_dials_;
133
+ std::unordered_map<Address, PendingDial> pending_dials_;
134
+ };
135
+
136
+ } // namespace librats
@@ -0,0 +1,98 @@
1
+ #include "librats/subsystems/ping_service.h"
2
+ #include "librats/node/node_context.h"
3
+
4
+ #include <cstring>
5
+
6
+ namespace librats {
7
+
8
+ namespace {
9
+ constexpr uint8_t kPing = 0;
10
+ constexpr uint8_t kPong = 1;
11
+ constexpr size_t kProbeSize = 1 + 8; // tag + 8-byte token
12
+
13
+ uint64_t now_ticks() {
14
+ return static_cast<uint64_t>(
15
+ std::chrono::steady_clock::now().time_since_epoch().count());
16
+ }
17
+ } // namespace
18
+
19
+ PingService::PingService(std::chrono::milliseconds interval) : interval_(interval) {}
20
+
21
+ PingService::~PingService() { stop(); }
22
+
23
+ void PingService::attach(NodeContext& ctx) {
24
+ network_ = &ctx.network;
25
+ network_->on(MessageType::Ping,
26
+ [this](const Peer& peer, ByteView payload) { handle(peer, payload); });
27
+ }
28
+
29
+ void PingService::start() {
30
+ if (running_.exchange(true)) return;
31
+ thread_ = std::thread(&PingService::run, this);
32
+ }
33
+
34
+ void PingService::stop() {
35
+ if (!running_.exchange(false)) return;
36
+ wake_.notify_all();
37
+ if (thread_.joinable()) thread_.join();
38
+ }
39
+
40
+ void PingService::run() {
41
+ while (running_.load()) {
42
+ std::unique_lock<std::mutex> lock(wait_mutex_);
43
+ wake_.wait_for(lock, interval_, [this] { return !running_.load(); });
44
+ if (!running_.load()) break;
45
+ lock.unlock();
46
+ ping_all();
47
+ }
48
+ }
49
+
50
+ void PingService::ping_all() {
51
+ if (!network_) return;
52
+
53
+ uint8_t probe[kProbeSize];
54
+ probe[0] = kPing;
55
+ const uint64_t token = now_ticks();
56
+ std::memcpy(probe + 1, &token, 8);
57
+
58
+ for (const PeerId& id : network_->connected_peers())
59
+ network_->send(id, MessageType::Ping, ByteView(probe, kProbeSize));
60
+ }
61
+
62
+ void PingService::handle(const Peer& peer, ByteView payload) {
63
+ if (payload.size() != kProbeSize) return;
64
+ const uint8_t tag = payload.data()[0];
65
+
66
+ if (tag == kPing) {
67
+ // Echo the probe straight back as a pong.
68
+ uint8_t pong[kProbeSize];
69
+ pong[0] = kPong;
70
+ std::memcpy(pong + 1, payload.data() + 1, 8);
71
+ network_->send(peer.id(), MessageType::Ping, ByteView(pong, kProbeSize));
72
+ return;
73
+ }
74
+
75
+ if (tag == kPong) {
76
+ uint64_t token = 0;
77
+ std::memcpy(&token, payload.data() + 1, 8);
78
+ const uint64_t elapsed_ticks = now_ticks() - token;
79
+ const auto rtt = std::chrono::duration_cast<std::chrono::milliseconds>(
80
+ std::chrono::steady_clock::duration(static_cast<std::chrono::steady_clock::rep>(elapsed_ticks)));
81
+ std::lock_guard<std::mutex> lock(mutex_);
82
+ rtt_[peer.id()] = rtt;
83
+ }
84
+ }
85
+
86
+ std::optional<std::chrono::milliseconds> PingService::last_rtt(const PeerId& id) const {
87
+ std::lock_guard<std::mutex> lock(mutex_);
88
+ auto it = rtt_.find(id);
89
+ if (it == rtt_.end()) return std::nullopt;
90
+ return it->second;
91
+ }
92
+
93
+ size_t PingService::alive_peer_count() const {
94
+ std::lock_guard<std::mutex> lock(mutex_);
95
+ return rtt_.size();
96
+ }
97
+
98
+ } // namespace librats
@@ -0,0 +1,66 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file ping_service.h
5
+ * @brief Liveness + round-trip-time probing, built purely on PeerNetwork.
6
+ *
7
+ * The first real subsystem on the redesigned plugin model — and the template for
8
+ * porting the larger ones. It depends on NOTHING but the PeerNetwork contract:
9
+ * no Node, no reactor internals, no `friend`. Its own thread periodically pings
10
+ * every connected peer; a peer echoes the probe back, letting the sender measure
11
+ * RTT. Easily mocked by handing it a fake PeerNetwork in tests.
12
+ *
13
+ * Wire format (MessageType::Ping payload): [tag:u8][token:8 bytes].
14
+ * tag 0 = ping, tag 1 = pong. The 8 token bytes are echoed back verbatim, so
15
+ * the original sender decodes them in its own format (endianness irrelevant).
16
+ */
17
+
18
+ #include "librats/util/rats_export.h"
19
+ #include "librats/node/peer_network.h"
20
+ #include "librats/peer/peer.h"
21
+ #include "librats/peer/peer_id.h"
22
+
23
+ #include <atomic>
24
+ #include <chrono>
25
+ #include <condition_variable>
26
+ #include <cstdint>
27
+ #include <mutex>
28
+ #include <optional>
29
+ #include <thread>
30
+ #include <unordered_map>
31
+
32
+ namespace librats {
33
+
34
+ class RATS_API PingService final : public Subsystem {
35
+ public:
36
+ explicit PingService(std::chrono::milliseconds interval = std::chrono::seconds(10));
37
+ ~PingService() override;
38
+
39
+ void attach(NodeContext& ctx) override;
40
+ void start() override;
41
+ void stop() override;
42
+
43
+ /// Most recent measured round-trip time to a peer, if one has been seen.
44
+ std::optional<std::chrono::milliseconds> last_rtt(const PeerId& id) const;
45
+
46
+ /// Number of peers we have received at least one pong from.
47
+ size_t alive_peer_count() const;
48
+
49
+ private:
50
+ void run(); ///< own thread: ping loop
51
+ void handle(const Peer& peer, ByteView payload);
52
+ void ping_all();
53
+
54
+ PeerNetwork* network_ = nullptr;
55
+ std::chrono::milliseconds interval_;
56
+
57
+ std::thread thread_;
58
+ std::atomic<bool> running_{false};
59
+ std::mutex wait_mutex_;
60
+ std::condition_variable wake_;
61
+
62
+ mutable std::mutex mutex_;
63
+ std::unordered_map<PeerId, std::chrono::milliseconds, PeerId::Hash> rtt_;
64
+ };
65
+
66
+ } // namespace librats