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,290 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file hole_punch.h
5
+ * @brief UDP hole punching: two nodes behind NATs dial each other at the same
6
+ * instant, arranged through a peer they both already have.
7
+ *
8
+ * ── Why this can work at all ────────────────────────────────────────────────
9
+ * A NAT drops an inbound datagram unless something went out to that peer first.
10
+ * So neither side can be dialed — but if BOTH dial at once, each one's outbound
11
+ * Syn opens the mapping and the filter its peer's Syn needs, and the two cross
12
+ * mid-path. Whichever direction survives becomes an ordinary connection.
13
+ *
14
+ * Everything below that is already in place and is deliberately left alone:
15
+ *
16
+ * - The punch packet is the Syn. A datagram dial sends one immediately
17
+ * (UdpMux::connect) and retries it, so a punch needs no new packet type on the
18
+ * wire — only a different retry shape (DialProfile::punch(): dense probes with
19
+ * no backoff, because the first Syns are *expected* to die on the peer's NAT).
20
+ * - One socket, one mapping. Every UDP peer shares the node's single datagram
21
+ * socket, so the port a peer sees us send from is the port anyone can aim at.
22
+ * That is what identify now reports on datagram links, and what nat_status.h
23
+ * turns into "is this mapping stable enough to punch through at all".
24
+ * - Both sides succeeding is fine. A punch normally lands in both directions,
25
+ * leaving two connections; PeerTable::add resolves that pair identically at
26
+ * both ends (the link from the smaller PeerId survives), exactly as it does for
27
+ * any simultaneous cross-connect.
28
+ *
29
+ * ── The only genuinely hard part: agreeing on "now" ─────────────────────────
30
+ * Punching works when the two bursts overlap, and the peers have no shared clock.
31
+ * The fix (as in libp2p's DCUtR) is to derive the instant from the round trip
32
+ * itself rather than from any clock. With A the initiator, B the target and R a
33
+ * peer both are connected to:
34
+ *
35
+ * 1. A →R→ B Connect{addrs of A} A notes the time it sent this
36
+ * 2. B →R→ A Connect{addrs of B} B now waits
37
+ * 3. A →R→ B Sync A starts dialing AT ONCE
38
+ * 4. B receives Sync B starts dialing AT ONCE
39
+ *
40
+ * A starts half a relayed round trip early and B starts on arrival, so the two
41
+ * bursts meet in the middle of the path. No clock synchronisation, and the accuracy
42
+ * degrades gracefully — a slow relay widens the window rather than breaking it,
43
+ * which is why the punch profile fires several probes instead of one.
44
+ *
45
+ * ── The relay ───────────────────────────────────────────────────────────────
46
+ * R is an ordinary node running this same subsystem. It forwards ONLY control
47
+ * messages, ONLY to a peer it is already connected to, and ONLY within a per-peer
48
+ * budget. It never opens a connection, never resolves an address and never carries
49
+ * application data — so it cannot be turned into an open reflector, and the traffic
50
+ * it does relay is a few dozen bytes per punch.
51
+ *
52
+ * ── Wire format (MessageType::Punch), big-endian ────────────────────────────
53
+ * envelope : [u8 ver=1][u8 op]
54
+ * op=0 Relay : [32B dst_id][inner…] sender → relay
55
+ * op=1 Relayed : [32B src_id][inner…] relay → destination
56
+ * inner : [u8 kind]
57
+ * kind=0 Connect : [u8 role][u8 n][ n × { u8 ip_len, ip bytes, u16 port } ]
58
+ * kind=1 Sync : —
59
+ *
60
+ * `role` says whether a Connect opens a rendezvous (0) or answers one (1). Without
61
+ * it the two are indistinguishable, and an initiator would read the answer it asked
62
+ * for as a competing rendezvous — hand the round to the peer, and the exchange would
63
+ * ping-pong Connects forever with neither side ever sending Sync. It is also what
64
+ * makes the collision case (both ends opening at once) recognisable, and therefore
65
+ * settleable by the same symmetric PeerId rule the peer table uses.
66
+ *
67
+ * The inner message is end-to-end between the two punching peers; the relay copies
68
+ * it across without looking inside. Every length is bounds-checked and every count
69
+ * capped, so a malformed or hostile payload is dropped rather than acted on.
70
+ *
71
+ * ── What this does NOT do ───────────────────────────────────────────────────
72
+ * A symmetric NAT (a fresh mapping per destination) cannot be punched through by
73
+ * any endpoint we can advertise, and this subsystem does not pretend otherwise: it
74
+ * checks NatMapping first and declines rather than firing a burst that cannot land.
75
+ * There is no TCP punching (the datagram wire is the one that fits), no port
76
+ * prediction (it would need more than the one socket the design is built on) and no
77
+ * relaying of data (a punch that fails, fails).
78
+ *
79
+ * Trust: the addresses a peer advertises for itself are its own unverified word, and
80
+ * a relay could forge a Connect naming somebody else's endpoint. Acting on that
81
+ * means sending a handful of 16-byte Syns to an address of another node's choosing —
82
+ * the same exposure PeerExchange already accepts when it dials what a peer told it
83
+ * about, and bounded here by the per-session address cap and the retry limits.
84
+ *
85
+ * Threading: message handlers run on reactor threads; one worker thread drives
86
+ * session timeouts and retries. All session state is behind one mutex, and no
87
+ * blocking work happens on a reactor thread.
88
+ */
89
+
90
+ #include "librats/util/rats_export.h"
91
+ #include "librats/core/address.h"
92
+ #include "librats/core/types.h"
93
+ #include "librats/node/nat_status.h"
94
+ #include "librats/node/peer_network.h"
95
+ #include "librats/peer/peer.h"
96
+ #include "librats/peer/peer_id.h"
97
+ #include "librats/subsystems/hole_punch_service.h"
98
+ #include "librats/subsystems/relay_service.h"
99
+
100
+ #include <atomic>
101
+ #include <chrono>
102
+ #include <condition_variable>
103
+ #include <cstdint>
104
+ #include <mutex>
105
+ #include <thread>
106
+ #include <unordered_map>
107
+ #include <vector>
108
+
109
+ namespace librats {
110
+
111
+ class DialService;
112
+ class ServiceRegistry;
113
+
114
+ /// Published as HolePunchService, so a module that discovers a peer it cannot dial
115
+ /// (PeerExchange) can hand the id over without depending on this class.
116
+ class RATS_API HolePunch final : public Subsystem, public HolePunchService {
117
+ public:
118
+ struct Config {
119
+ /// Peers asked to carry one punch's rendezvous. More than one because we
120
+ /// cannot know which of our peers also has the target; each relay that does
121
+ /// not simply drops the request. Small, because every extra relay is a
122
+ /// duplicate rendezvous the target has to recognise and ignore.
123
+ size_t max_relays = 3;
124
+
125
+ /// Endpoints advertised to the target, and therefore dialed by it. Capped:
126
+ /// this is the fan-out one peer's claim can make us produce.
127
+ size_t max_addresses = 4;
128
+
129
+ /// Rendezvous rounds before a target is given up on for `cooldown`.
130
+ int attempts = 3;
131
+
132
+ /// How long one round may take from the first Connect to a live connection.
133
+ /// Covers two relayed hops plus the punch burst itself.
134
+ std::chrono::milliseconds round_timeout{6000};
135
+
136
+ /// How long after giving up before the same target may be punched again.
137
+ /// Only a rendezvous WE opened ever ends in one: a round we merely answered
138
+ /// timing out says nothing about the target, and calling it off there would
139
+ /// silently drop the retries the initiator is already sending.
140
+ std::chrono::milliseconds cooldown{60000};
141
+
142
+ /// Concurrent punch sessions. Bounds both memory and how many dial bursts
143
+ /// this node can be talked into producing at once.
144
+ size_t max_sessions = 32;
145
+
146
+ /// Serve as a rendezvous for other peers' punches. Cheap (a few dozen bytes
147
+ /// forwarded per punch) but it is still work done on somebody else's behalf,
148
+ /// so it is a choice rather than an assumption.
149
+ bool enable_relay = true;
150
+
151
+ /// Relay budget: messages one peer may have forwarded per window. A punch
152
+ /// costs two forwarded messages per round, so the default leaves generous
153
+ /// room for honest use while capping what one peer can spend us.
154
+ size_t relay_budget = 12;
155
+ std::chrono::milliseconds relay_window{1000};
156
+
157
+ /// Refuse to punch when the mesh says our own mapping is per-destination
158
+ /// (symmetric NAT): no endpoint we could advertise would be the one the
159
+ /// target's packets arrive on, so the burst cannot land. Turn off only to
160
+ /// try anyway (a NAT can be misclassified by an unlucky sample).
161
+ bool skip_when_endpoint_dependent = true;
162
+
163
+ /// How hard each punch dial retries its Syn.
164
+ DialProfile profile = DialProfile::punch();
165
+
166
+ /// Session bookkeeping cadence — retries, timeouts, cooldown expiry.
167
+ std::chrono::milliseconds tick{250};
168
+
169
+ /// When a punch cannot be attempted at all, or has been given up on, hand
170
+ /// the target to RelayService — the next rung down the ladder (see
171
+ /// subsystems/relay.h). Costs nothing when no Relay is attached: the
172
+ /// service simply does not resolve.
173
+ bool relay_on_failure = true;
174
+ };
175
+
176
+ HolePunch();
177
+ explicit HolePunch(Config config);
178
+ ~HolePunch() override;
179
+
180
+ void attach(NodeContext& ctx) override;
181
+ void start() override;
182
+ void stop() override;
183
+
184
+ /// Try to reach `target` by punching. Non-blocking: the rendezvous runs in the
185
+ /// background and a successful punch surfaces as an ordinary peer-connected
186
+ /// event. A no-op when already connected to the target, when a session for it
187
+ /// is already running, when it is still in cooldown, or when this node has
188
+ /// nothing punchable to advertise (see nat_status.h).
189
+ /// @return whether a session was actually started.
190
+ bool punch(const PeerId& target) override;
191
+
192
+ /// Sessions currently in flight (diagnostics and tests).
193
+ size_t active_sessions() const;
194
+ /// Punch bursts this node has fired (diagnostics and tests).
195
+ uint64_t punches_started() const noexcept { return punches_started_.load(); }
196
+ /// Messages forwarded on behalf of other peers (diagnostics and tests).
197
+ uint64_t relayed() const noexcept { return relayed_.load(); }
198
+
199
+ private:
200
+ enum class Phase : uint8_t {
201
+ AwaitingPeerConnect, ///< initiator: our Connect is out, waiting for theirs
202
+ AwaitingSync, ///< responder: our Connect is out, waiting for Sync
203
+ Punching, ///< the burst is in flight
204
+ };
205
+
206
+ struct Session {
207
+ bool initiator = false;
208
+ Phase phase = Phase::AwaitingPeerConnect;
209
+ int attempt = 0;
210
+ std::vector<Address> peer_addresses;
211
+ std::chrono::steady_clock::time_point deadline{};
212
+ /// The relay that last carried something from the target, if any. Once one
213
+ /// hop is known to work, the rest of the exchange goes back the same way —
214
+ /// it is the one path proven to reach the target, and it keeps the fan-out
215
+ /// (and the load on peers that cannot help) to the first message alone.
216
+ PeerId via{};
217
+ bool have_via = false;
218
+ };
219
+
220
+ // — message handling (reactor threads) —
221
+ void handle(const Peer& from, ByteView payload);
222
+ void handle_relay_request(const Peer& from, const PeerId& dst, ByteView inner);
223
+ void handle_relayed(const Peer& via, const PeerId& src, ByteView inner);
224
+ /// @param opening whether the sender is opening a rendezvous (rather than
225
+ /// answering ours) — see the `role` byte in the wire format above.
226
+ void handle_connect(const Peer& via, const PeerId& src, bool opening,
227
+ std::vector<Address> addresses);
228
+ void handle_sync(const PeerId& src);
229
+
230
+ // — outgoing —
231
+ /// Wrap `inner` in a Relay envelope and hand it to peers that might reach `dst`:
232
+ /// just `via` when a working hop is already known, otherwise up to max_relays of
233
+ /// our peers. Returns how many were given it.
234
+ size_t relay_to(const PeerId& dst, const Bytes& inner, const PeerId* via);
235
+ bool send_connect(const PeerId& target, bool opening, const PeerId* via);
236
+ void send_sync(const PeerId& target, const PeerId* via);
237
+ /// Fire the dial burst at everything the peer advertised. Caller must NOT hold
238
+ /// the mutex: dialing reaches the reactor, which may run callbacks inline.
239
+ void fire_punch(const PeerId& target, const std::vector<Address>& addresses);
240
+
241
+ // — worker —
242
+ void loop();
243
+ void service_sessions();
244
+
245
+ // — helpers —
246
+ /// Hand `target` to the relay module, if one is attached and the fallback is
247
+ /// on. Called when a punch is impossible or has run out of attempts — the
248
+ /// point at which a relayed path stops being the worse option and becomes the
249
+ /// only one. Retires the target's cooldown when the relay takes it on, so that
250
+ /// the upgrade punch a circuit asks for is not refused by the very give-up that
251
+ /// produced the circuit. Caller must NOT hold the mutex.
252
+ void escalate_to_relay(const PeerId& target);
253
+ /// Peers reachable over a DIRECT link. A relayed peer is deliberately not one:
254
+ /// a punch to it is an upgrade in progress, and counting the circuit as success
255
+ /// would retire the session before it had done anything.
256
+ std::vector<PeerId> directly_connected() const;
257
+ std::vector<Address> own_punch_addresses() const;
258
+ bool relay_budget_ok(const PeerId& from);
259
+ bool in_cooldown(const PeerId& target) const; ///< caller holds mutex_
260
+ void begin_cooldown(const PeerId& target); ///< caller holds mutex_
261
+
262
+ Config config_;
263
+ PeerNetwork* network_ = nullptr;
264
+ DialService* dialer_ = nullptr;
265
+ ExternalAddressService* external_ = nullptr;
266
+ ServiceRegistry* services_ = nullptr;
267
+ /// Resolved in start(), not attach(): Relay may be attached after us, and every
268
+ /// attach() runs before any start(). Atomic because it is read from reactor
269
+ /// threads and the worker alike.
270
+ std::atomic<RelayService*> relay_{nullptr};
271
+
272
+ std::atomic<bool> running_{false};
273
+ std::atomic<uint64_t> punches_started_{0};
274
+ std::atomic<uint64_t> relayed_{0};
275
+
276
+ mutable std::mutex mutex_;
277
+ std::condition_variable cv_;
278
+ std::thread worker_;
279
+ std::unordered_map<PeerId, Session, PeerId::Hash> sessions_;
280
+ std::unordered_map<PeerId, std::chrono::steady_clock::time_point, PeerId::Hash> cooldown_;
281
+
282
+ struct RelayBudget {
283
+ std::chrono::steady_clock::time_point window_started{};
284
+ size_t spent = 0;
285
+ };
286
+ std::mutex relay_mutex_;
287
+ std::unordered_map<PeerId, RelayBudget, PeerId::Hash> relay_budget_;
288
+ };
289
+
290
+ } // namespace librats
@@ -0,0 +1,38 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file hole_punch_service.h
5
+ * @brief Capability that lets a sibling module ask for a NAT hole punch by PeerId.
6
+ *
7
+ * Published by HolePunch via ServiceRegistry (see service_registry.h). A discovery
8
+ * module knows *who* it failed to reach long before it knows why — PeerExchange, for
9
+ * instance, learns a peer's id together with an address that then refuses to dial —
10
+ * and this is the whole contract it needs to hand that id over: no addresses, no
11
+ * relay selection, no NAT reasoning, all of which are HolePunch's own business.
12
+ *
13
+ * Resolving it returns nullptr when hole punching is not enabled, which is exactly
14
+ * what makes the fallback optional at the consumer's side:
15
+ *
16
+ * if (auto* punch = ctx.services.get<HolePunchService>()) punch->punch(id);
17
+ *
18
+ * The provider registers during attach(), so resolve it in start() (every attach()
19
+ * runs before any start()) rather than in your own attach(), where the provider may
20
+ * not have been attached yet. The pointer is NON-owning and valid while the node is.
21
+ */
22
+
23
+ #include "librats/util/rats_export.h"
24
+ #include "librats/peer/peer_id.h"
25
+
26
+ namespace librats {
27
+
28
+ struct RATS_API HolePunchService {
29
+ virtual ~HolePunchService() = default;
30
+
31
+ /// Try to reach `target` by punching. Non-blocking; a successful punch surfaces
32
+ /// as an ordinary peer-connected event. @return whether a rendezvous actually
33
+ /// started — false is routine (already connected, already punching, in cooldown,
34
+ /// or nothing punchable to advertise) and needs no handling by the caller.
35
+ virtual bool punch(const PeerId& target) = 0;
36
+ };
37
+
38
+ } // namespace librats
@@ -0,0 +1,66 @@
1
+ #include "librats/subsystems/mdns_discovery.h"
2
+ #include "librats/node/node_context.h"
3
+ #include "librats/util/logger.h"
4
+
5
+ namespace librats {
6
+
7
+ MdnsDiscovery::MdnsDiscovery() : MdnsDiscovery(Config()) {}
8
+
9
+ MdnsDiscovery::MdnsDiscovery(Config config) : config_(std::move(config)) {}
10
+
11
+ MdnsDiscovery::~MdnsDiscovery() { stop(); }
12
+
13
+ void MdnsDiscovery::attach(NodeContext& ctx) { network_ = &ctx.network; }
14
+
15
+ void MdnsDiscovery::start() {
16
+ if (running_.exchange(true)) return;
17
+
18
+ instance_ = config_.instance_name.empty() ? ("rats-" + network_->local_id().short_hex())
19
+ : config_.instance_name;
20
+ const uint16_t port = network_->listen_port();
21
+
22
+ mdns_ = std::make_unique<MdnsClient>(instance_, port);
23
+ mdns_->set_service_callback([this](const MdnsService& service, bool is_new) { on_service(service, is_new); });
24
+ if (!mdns_->start()) {
25
+ LOG_ERROR("mdns-discovery", "Failed to start mDNS client");
26
+ running_.store(false);
27
+ mdns_.reset();
28
+ return;
29
+ }
30
+ mdns_->announce_service(instance_, port);
31
+ mdns_->start_discovery();
32
+ LOG_INFO("mdns-discovery", "Announcing '" << instance_ << "' on port " << port);
33
+ }
34
+
35
+ void MdnsDiscovery::stop() {
36
+ if (!running_.exchange(false)) return;
37
+ if (mdns_) {
38
+ mdns_->stop_discovery();
39
+ mdns_->stop_announcing();
40
+ mdns_->stop();
41
+ mdns_.reset();
42
+ }
43
+ }
44
+
45
+ bool MdnsDiscovery::is_running() const { return running_.load() && mdns_ && mdns_->is_running(); }
46
+
47
+ void MdnsDiscovery::on_service(const MdnsService& service, bool /*is_new*/) {
48
+ if (service.ip_address.empty() || service.port == 0) return;
49
+ // Skip our own announcement (its name carries our instance label).
50
+ if (service.service_name.find(instance_) != std::string::npos) return;
51
+
52
+ // service.ip_address comes off the wire (untrusted); only dial a valid numeric IP.
53
+ const auto ip = IpAddress::parse(service.ip_address);
54
+ if (!ip) return;
55
+
56
+ const Address addr{*ip, service.port};
57
+ {
58
+ std::lock_guard<std::mutex> lock(dialed_mutex_);
59
+ if (!dialed_.insert(addr).second) return; // already dialed this address
60
+ }
61
+ LOG_DEBUG("mdns-discovery", "Dialing discovered service " << service.service_name
62
+ << " at " << addr.to_string());
63
+ network_->connect(addr);
64
+ }
65
+
66
+ } // namespace librats
@@ -0,0 +1,55 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file mdns_discovery.h
5
+ * @brief Local-network peer discovery via mDNS — a thin adapter, not a rewrite.
6
+ *
7
+ * Wraps the existing MdnsClient (src/mdns.h) as a Subsystem WITHOUT modifying it.
8
+ * On start it announces our TCP listen port as an mDNS service and browses for
9
+ * the same service type, dialing discovered instances through the node. Each
10
+ * node uses a unique instance name (derived from its PeerId) so two nodes on the
11
+ * same host don't collide and can filter out their own announcement.
12
+ */
13
+
14
+ #include "librats/util/rats_export.h"
15
+ #include "librats/node/peer_network.h"
16
+ #include "librats/mdns/mdns.h"
17
+
18
+ #include <atomic>
19
+ #include <memory>
20
+ #include <mutex>
21
+ #include <string>
22
+ #include <unordered_set>
23
+
24
+ namespace librats {
25
+
26
+ class RATS_API MdnsDiscovery final : public Subsystem {
27
+ public:
28
+ struct Config {
29
+ std::string instance_name = ""; ///< empty → derived from our PeerId
30
+ };
31
+
32
+ MdnsDiscovery();
33
+ explicit MdnsDiscovery(Config config);
34
+ ~MdnsDiscovery() override;
35
+
36
+ void attach(NodeContext& ctx) override;
37
+ void start() override;
38
+ void stop() override;
39
+
40
+ bool is_running() const;
41
+
42
+ private:
43
+ void on_service(const MdnsService& service, bool is_new);
44
+
45
+ Config config_;
46
+ std::string instance_;
47
+ PeerNetwork* network_ = nullptr;
48
+ std::unique_ptr<MdnsClient> mdns_;
49
+ std::atomic<bool> running_{false};
50
+
51
+ std::mutex dialed_mutex_;
52
+ std::unordered_set<Address> dialed_; ///< peers we've already dialed
53
+ };
54
+
55
+ } // namespace librats
@@ -0,0 +1,112 @@
1
+ #include "librats/subsystems/message_json.h"
2
+ #include "librats/node/node_context.h"
3
+ #include "librats/util/logger.h"
4
+
5
+ #include <algorithm>
6
+
7
+ namespace librats {
8
+
9
+ void MessageJson::attach(NodeContext& ctx) {
10
+ network_ = &ctx.network;
11
+ network_->on(MessageType::Typed,
12
+ [this](const Peer& peer, ByteView payload) { on_typed(peer.id(), payload); });
13
+ }
14
+
15
+ // ── Registration ────────────────────────────────────────────────────────────
16
+
17
+ void MessageJson::on(const std::string& type, Handler handler) {
18
+ std::lock_guard<std::mutex> lock(mutex_);
19
+ handlers_[type].push_back({std::move(handler), /*once=*/false});
20
+ }
21
+
22
+ void MessageJson::once(const std::string& type, Handler handler) {
23
+ std::lock_guard<std::mutex> lock(mutex_);
24
+ handlers_[type].push_back({std::move(handler), /*once=*/true});
25
+ }
26
+
27
+ void MessageJson::off(const std::string& type) {
28
+ std::lock_guard<std::mutex> lock(mutex_);
29
+ handlers_.erase(type);
30
+ }
31
+
32
+ // ── Sending ─────────────────────────────────────────────────────────────────
33
+
34
+ Bytes MessageJson::encode(const std::string& type, const librats::Json& data) {
35
+ const std::string body = data.dump();
36
+ Bytes out;
37
+ out.reserve(2 + type.size() + body.size());
38
+ out.push_back(static_cast<uint8_t>((type.size() >> 8) & 0xFF));
39
+ out.push_back(static_cast<uint8_t>(type.size() & 0xFF));
40
+ out.insert(out.end(), type.begin(), type.end());
41
+ out.insert(out.end(), body.begin(), body.end());
42
+ return out;
43
+ }
44
+
45
+ void MessageJson::send(const std::string& type, const librats::Json& data, SendCallback cb) {
46
+ if (!network_) { if (cb) cb(false, "not attached to a network"); return; }
47
+
48
+ const Bytes payload = encode(type, data);
49
+ const size_t n = network_->connected_peers().size();
50
+ network_->broadcast(MessageType::Typed, ByteView(payload));
51
+ if (cb) cb(n > 0, n > 0 ? "" : "no connected peers");
52
+ }
53
+
54
+ void MessageJson::send(const PeerId& to, const std::string& type, const librats::Json& data,
55
+ SendCallback cb) {
56
+ if (!network_) { if (cb) cb(false, "not attached to a network"); return; }
57
+
58
+ // A send() to an unknown peer is a safe no-op, so only pay for the directory
59
+ // lookup when the caller actually wants a delivery verdict.
60
+ if (cb) {
61
+ const auto peers = network_->connected_peers();
62
+ if (std::find(peers.begin(), peers.end(), to) == peers.end()) {
63
+ cb(false, "peer not connected");
64
+ return;
65
+ }
66
+ }
67
+ network_->send(to, MessageType::Typed, ByteView(encode(type, data)));
68
+ if (cb) cb(true, "");
69
+ }
70
+
71
+ // ── Receiving (reactor thread) ──────────────────────────────────────────────
72
+
73
+ void MessageJson::on_typed(const PeerId& from, ByteView payload) {
74
+ const uint8_t* p = payload.data();
75
+ const size_t n = payload.size();
76
+ if (n < 2) return;
77
+
78
+ const size_t type_len = (static_cast<size_t>(p[0]) << 8) | p[1];
79
+ if (2 + type_len > n) return;
80
+
81
+ std::string type(reinterpret_cast<const char*>(p + 2), type_len);
82
+ librats::Json data = librats::Json::parse(p + 2 + type_len, p + n, nullptr, /*allow_exceptions=*/false);
83
+ if (data.is_discarded()) {
84
+ LOG_WARN("msgex", "Malformed JSON for type '" << type << "' from " << from.short_hex());
85
+ return;
86
+ }
87
+
88
+ // Snapshot the handlers under the lock and drop the one-shot ones, then invoke
89
+ // outside the lock so a handler may freely (un)register without deadlocking.
90
+ std::vector<Entry> to_call;
91
+ {
92
+ std::lock_guard<std::mutex> lock(mutex_);
93
+ auto it = handlers_.find(type);
94
+ if (it == handlers_.end()) return;
95
+ to_call = it->second;
96
+ auto& vec = it->second;
97
+ vec.erase(std::remove_if(vec.begin(), vec.end(), [](const Entry& e) { return e.once; }), vec.end());
98
+ if (vec.empty()) handlers_.erase(it);
99
+ }
100
+
101
+ for (const Entry& e : to_call) {
102
+ try {
103
+ e.handler(from, data);
104
+ } catch (const std::exception& ex) {
105
+ LOG_ERROR("msgex", "Handler for '" << type << "' threw: " << ex.what());
106
+ } catch (...) {
107
+ LOG_ERROR("msgex", "Handler for '" << type << "' threw an unknown exception");
108
+ }
109
+ }
110
+ }
111
+
112
+ } // namespace librats
@@ -0,0 +1,88 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file message_json.h
5
+ * @brief Typed JSON message exchange over PeerNetwork.
6
+ *
7
+ * A familiar `on`/`once`/`off` + `send` messaging API as a Subsystem: an
8
+ * application names a message *type* (a string) and exchanges librats::Json
9
+ * payloads with peers, without caring about framing or channels. Attach it like
10
+ * any subsystem; reach it later via node.json() (or subsystem<MessageJson>()):
11
+ *
12
+ * node.add_subsystem(std::make_unique<MessageJson>());
13
+ * node.json()->on("chat", [](const PeerId& from, const json& j){ ... });
14
+ * node.start();
15
+ * node.json()->send(peer_id, "chat", json{{"text","hi"}});
16
+ *
17
+ * Two deliberate properties:
18
+ * - the sender is the *authenticated* PeerId from the handshake, not a
19
+ * self-reported field in the payload (which could be spoofed);
20
+ * - no JSON envelope on the wire — just [type][payload], encrypted by the
21
+ * transport like everything else.
22
+ *
23
+ * Wire format (MessageType::Typed payload):
24
+ * [type_len:u16][type bytes][json payload bytes] (json as compact text)
25
+ *
26
+ * Handlers run on a reactor thread; do not block in them. Registration is
27
+ * thread-safe and may happen before or after start().
28
+ */
29
+
30
+ #include "librats/util/rats_export.h"
31
+ #include "librats/node/peer_network.h"
32
+ #include "librats/peer/peer.h"
33
+ #include "librats/peer/peer_id.h"
34
+ #include "librats/core/bytes.h"
35
+ #include "librats/util/json.h"
36
+
37
+ #include <functional>
38
+ #include <mutex>
39
+ #include <string>
40
+ #include <unordered_map>
41
+ #include <vector>
42
+
43
+ namespace librats {
44
+
45
+ class RATS_API MessageJson final : public Subsystem {
46
+ public:
47
+ using Handler = std::function<void(const PeerId& from, const librats::Json& data)>;
48
+ using SendCallback = std::function<void(bool ok, const std::string& error)>;
49
+
50
+ /// Register a handler for `type`. Additive: multiple handlers may coexist and
51
+ /// all fire (in registration order) for each received message of that type.
52
+ void on(const std::string& type, Handler handler);
53
+
54
+ /// Like on(), but the handler is removed right after it fires once.
55
+ void once(const std::string& type, Handler handler);
56
+
57
+ /// Remove every handler registered for `type`.
58
+ void off(const std::string& type);
59
+
60
+ /// Broadcast `data` of `type` to all connected peers. `cb`, if given, reports
61
+ /// whether there was at least one peer to send to.
62
+ void send(const std::string& type, const librats::Json& data, SendCallback cb = nullptr);
63
+
64
+ /// Send `data` of `type` to one peer. `cb`, if given, reports success or the
65
+ /// reason it could not be sent (e.g. the peer is not connected).
66
+ void send(const PeerId& to, const std::string& type, const librats::Json& data,
67
+ SendCallback cb = nullptr);
68
+
69
+ // Subsystem — no background thread; purely event-driven.
70
+ void attach(NodeContext& ctx) override;
71
+ void start() override {}
72
+ void stop() override {}
73
+
74
+ private:
75
+ void on_typed(const PeerId& from, ByteView payload);
76
+ static Bytes encode(const std::string& type, const librats::Json& data);
77
+
78
+ struct Entry {
79
+ Handler handler;
80
+ bool once;
81
+ };
82
+
83
+ PeerNetwork* network_ = nullptr;
84
+ mutable std::mutex mutex_;
85
+ std::unordered_map<std::string, std::vector<Entry>> handlers_;
86
+ };
87
+
88
+ } // namespace librats