librats 1.0.2 → 2.1.4

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 (313) hide show
  1. package/README.md +137 -332
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +271 -696
  4. package/lib/index.js +383 -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 +396 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +731 -0
  10. package/native-src/src/librats/bindings/rats.h +352 -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 +42 -0
  73. package/native-src/src/librats/core/types.h +93 -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/config.h +110 -0
  144. package/native-src/src/librats/node/dial_service.h +54 -0
  145. package/native-src/src/librats/node/dialer.cpp +264 -0
  146. package/native-src/src/librats/node/dialer.h +188 -0
  147. package/native-src/src/librats/node/host_events.h +26 -0
  148. package/native-src/src/librats/node/identify.cpp +130 -0
  149. package/native-src/src/librats/node/identify.h +71 -0
  150. package/native-src/src/librats/node/nat_status.cpp +103 -0
  151. package/native-src/src/librats/node/nat_status.h +118 -0
  152. package/native-src/src/librats/node/node.cpp +826 -0
  153. package/native-src/src/librats/node/node.h +333 -0
  154. package/native-src/src/librats/node/node_context.h +33 -0
  155. package/native-src/src/librats/node/peer_network.h +91 -0
  156. package/native-src/src/librats/peer/peer.h +49 -0
  157. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  158. package/native-src/src/librats/peer/peer_book.h +88 -0
  159. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  160. package/native-src/src/librats/peer/peer_id.h +62 -0
  161. package/native-src/src/librats/peer/peer_info.h +37 -0
  162. package/native-src/src/librats/peer/peer_table.cpp +137 -0
  163. package/native-src/src/librats/peer/peer_table.h +148 -0
  164. package/native-src/src/librats/security/handshaker.h +66 -0
  165. package/native-src/src/librats/security/identity.h +43 -0
  166. package/native-src/src/librats/security/noise_security.cpp +122 -0
  167. package/native-src/src/librats/security/noise_security.h +37 -0
  168. package/native-src/src/librats/security/plaintext_security.h +106 -0
  169. package/native-src/src/librats/security/session.h +37 -0
  170. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  171. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  172. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  173. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  174. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  176. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  177. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  178. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  179. package/native-src/src/librats/subsystems/hole_punch.cpp +532 -0
  180. package/native-src/src/librats/subsystems/hole_punch.h +266 -0
  181. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  182. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  184. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  185. package/native-src/src/librats/subsystems/message_json.h +88 -0
  186. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  188. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  189. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  190. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  192. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  193. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  194. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  195. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  196. package/native-src/src/librats/transport/connection.cpp +343 -0
  197. package/native-src/src/librats/transport/connection.h +283 -0
  198. package/native-src/src/librats/transport/link.h +96 -0
  199. package/native-src/src/librats/transport/reactor.cpp +540 -0
  200. package/native-src/src/librats/transport/reactor.h +224 -0
  201. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  202. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  203. package/native-src/src/librats/transport/tcp_link.h +43 -0
  204. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  205. package/native-src/src/librats/transport/udp_mux.h +363 -0
  206. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  207. package/native-src/src/librats/transport/udp_packet.h +190 -0
  208. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  209. package/native-src/src/librats/transport/udp_stream.h +614 -0
  210. package/native-src/src/librats/util/features.h.in +51 -0
  211. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  212. package/native-src/src/librats/util/fs.h +136 -0
  213. package/native-src/src/librats/util/json.cpp +1002 -0
  214. package/native-src/src/librats/util/json.h +444 -0
  215. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  216. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  217. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  218. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  219. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  220. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  221. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  222. package/native-src/src/librats/util/rats_export.h +69 -0
  223. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  224. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  225. package/native-src/src/librats/wire/frame.cpp +76 -0
  226. package/native-src/src/librats/wire/frame.h +110 -0
  227. package/native-src/src/librats/wire/message_router.cpp +45 -0
  228. package/native-src/src/librats/wire/message_router.h +47 -0
  229. package/package.json +5 -4
  230. package/scripts/build-librats.js +1 -0
  231. package/scripts/postinstall.js +3 -3
  232. package/scripts/prepare-package.js +4 -4
  233. package/scripts/verify-installation.js +63 -105
  234. package/src/librats_node.cpp +1042 -1324
  235. package/native-src/src/bencode.cpp +0 -485
  236. package/native-src/src/bencode.h +0 -145
  237. package/native-src/src/bittorrent.cpp +0 -14
  238. package/native-src/src/bittorrent.h +0 -74
  239. package/native-src/src/bt_bitfield.cpp +0 -372
  240. package/native-src/src/bt_bitfield.h +0 -316
  241. package/native-src/src/bt_choker.cpp +0 -228
  242. package/native-src/src/bt_choker.h +0 -147
  243. package/native-src/src/bt_client.cpp +0 -1047
  244. package/native-src/src/bt_client.h +0 -445
  245. package/native-src/src/bt_create_torrent.cpp +0 -677
  246. package/native-src/src/bt_create_torrent.h +0 -473
  247. package/native-src/src/bt_extension.cpp +0 -469
  248. package/native-src/src/bt_extension.h +0 -309
  249. package/native-src/src/bt_file_storage.cpp +0 -261
  250. package/native-src/src/bt_file_storage.h +0 -298
  251. package/native-src/src/bt_handshake.cpp +0 -134
  252. package/native-src/src/bt_handshake.h +0 -157
  253. package/native-src/src/bt_messages.cpp +0 -364
  254. package/native-src/src/bt_messages.h +0 -324
  255. package/native-src/src/bt_network.cpp +0 -1007
  256. package/native-src/src/bt_network.h +0 -417
  257. package/native-src/src/bt_peer_connection.cpp +0 -742
  258. package/native-src/src/bt_peer_connection.h +0 -592
  259. package/native-src/src/bt_piece_picker.cpp +0 -786
  260. package/native-src/src/bt_piece_picker.h +0 -473
  261. package/native-src/src/bt_resume_data.cpp +0 -410
  262. package/native-src/src/bt_resume_data.h +0 -249
  263. package/native-src/src/bt_torrent.cpp +0 -2120
  264. package/native-src/src/bt_torrent.h +0 -641
  265. package/native-src/src/bt_torrent_info.cpp +0 -659
  266. package/native-src/src/bt_torrent_info.h +0 -418
  267. package/native-src/src/bt_types.h +0 -621
  268. package/native-src/src/chained_send_buffer.cpp +0 -75
  269. package/native-src/src/chained_send_buffer.h +0 -137
  270. package/native-src/src/crypto/hkdf.c +0 -266
  271. package/native-src/src/crypto/poly1305.h +0 -36
  272. package/native-src/src/dht.cpp +0 -3311
  273. package/native-src/src/dht.h +0 -717
  274. package/native-src/src/disk_io.cpp +0 -632
  275. package/native-src/src/disk_io.h +0 -315
  276. package/native-src/src/file_transfer.cpp +0 -1415
  277. package/native-src/src/file_transfer.h +0 -286
  278. package/native-src/src/fs.h +0 -108
  279. package/native-src/src/gossipsub.cpp +0 -1139
  280. package/native-src/src/gossipsub.h +0 -403
  281. package/native-src/src/ice.cpp +0 -893
  282. package/native-src/src/ice.h +0 -559
  283. package/native-src/src/json.hpp +0 -25526
  284. package/native-src/src/librats.cpp +0 -2378
  285. package/native-src/src/librats.h +0 -2324
  286. package/native-src/src/librats_bittorrent.cpp +0 -601
  287. package/native-src/src/librats_c.cpp +0 -1557
  288. package/native-src/src/librats_c.h +0 -323
  289. package/native-src/src/librats_discovery.cpp +0 -402
  290. package/native-src/src/librats_encryption.cpp +0 -275
  291. package/native-src/src/librats_file_transfer.cpp +0 -144
  292. package/native-src/src/librats_gossipsub.cpp +0 -289
  293. package/native-src/src/librats_ice.cpp +0 -213
  294. package/native-src/src/librats_log_macros.h +0 -36
  295. package/native-src/src/librats_logging.cpp +0 -173
  296. package/native-src/src/librats_mdns.cpp +0 -166
  297. package/native-src/src/librats_persistence.cpp +0 -796
  298. package/native-src/src/librats_portmap.cpp +0 -419
  299. package/native-src/src/librats_reconnection.cpp +0 -218
  300. package/native-src/src/librats_statistic.cpp +0 -105
  301. package/native-src/src/librats_storage.cpp +0 -189
  302. package/native-src/src/rats_export.h +0 -17
  303. package/native-src/src/receive_buffer.cpp +0 -82
  304. package/native-src/src/receive_buffer.h +0 -127
  305. package/native-src/src/socket.h +0 -228
  306. package/native-src/src/threadmanager.cpp +0 -105
  307. package/native-src/src/threadmanager.h +0 -53
  308. package/native-src/src/tracker.cpp +0 -1264
  309. package/native-src/src/tracker.h +0 -319
  310. package/native-src/src/turn.cpp +0 -762
  311. package/native-src/src/turn.h +0 -460
  312. package/native-src/src/wakeup_pipe.h +0 -60
  313. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,266 @@
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
+
99
+ #include <atomic>
100
+ #include <chrono>
101
+ #include <condition_variable>
102
+ #include <cstdint>
103
+ #include <mutex>
104
+ #include <thread>
105
+ #include <unordered_map>
106
+ #include <vector>
107
+
108
+ namespace librats {
109
+
110
+ class DialService;
111
+
112
+ /// Published as HolePunchService, so a module that discovers a peer it cannot dial
113
+ /// (PeerExchange) can hand the id over without depending on this class.
114
+ class RATS_API HolePunch final : public Subsystem, public HolePunchService {
115
+ public:
116
+ struct Config {
117
+ /// Peers asked to carry one punch's rendezvous. More than one because we
118
+ /// cannot know which of our peers also has the target; each relay that does
119
+ /// not simply drops the request. Small, because every extra relay is a
120
+ /// duplicate rendezvous the target has to recognise and ignore.
121
+ size_t max_relays = 3;
122
+
123
+ /// Endpoints advertised to the target, and therefore dialed by it. Capped:
124
+ /// this is the fan-out one peer's claim can make us produce.
125
+ size_t max_addresses = 4;
126
+
127
+ /// Rendezvous rounds before a target is given up on for `cooldown`.
128
+ int attempts = 3;
129
+
130
+ /// How long one round may take from the first Connect to a live connection.
131
+ /// Covers two relayed hops plus the punch burst itself.
132
+ std::chrono::milliseconds round_timeout{6000};
133
+
134
+ /// How long after giving up before the same target may be punched again.
135
+ /// Only a rendezvous WE opened ever ends in one: a round we merely answered
136
+ /// timing out says nothing about the target, and calling it off there would
137
+ /// silently drop the retries the initiator is already sending.
138
+ std::chrono::milliseconds cooldown{60000};
139
+
140
+ /// Concurrent punch sessions. Bounds both memory and how many dial bursts
141
+ /// this node can be talked into producing at once.
142
+ size_t max_sessions = 32;
143
+
144
+ /// Serve as a rendezvous for other peers' punches. Cheap (a few dozen bytes
145
+ /// forwarded per punch) but it is still work done on somebody else's behalf,
146
+ /// so it is a choice rather than an assumption.
147
+ bool enable_relay = true;
148
+
149
+ /// Relay budget: messages one peer may have forwarded per window. A punch
150
+ /// costs two forwarded messages per round, so the default leaves generous
151
+ /// room for honest use while capping what one peer can spend us.
152
+ size_t relay_budget = 12;
153
+ std::chrono::milliseconds relay_window{1000};
154
+
155
+ /// Refuse to punch when the mesh says our own mapping is per-destination
156
+ /// (symmetric NAT): no endpoint we could advertise would be the one the
157
+ /// target's packets arrive on, so the burst cannot land. Turn off only to
158
+ /// try anyway (a NAT can be misclassified by an unlucky sample).
159
+ bool skip_when_endpoint_dependent = true;
160
+
161
+ /// How hard each punch dial retries its Syn.
162
+ DialProfile profile = DialProfile::punch();
163
+
164
+ /// Session bookkeeping cadence — retries, timeouts, cooldown expiry.
165
+ std::chrono::milliseconds tick{250};
166
+ };
167
+
168
+ HolePunch();
169
+ explicit HolePunch(Config config);
170
+ ~HolePunch() override;
171
+
172
+ void attach(NodeContext& ctx) override;
173
+ void start() override;
174
+ void stop() override;
175
+
176
+ /// Try to reach `target` by punching. Non-blocking: the rendezvous runs in the
177
+ /// background and a successful punch surfaces as an ordinary peer-connected
178
+ /// event. A no-op when already connected to the target, when a session for it
179
+ /// is already running, when it is still in cooldown, or when this node has
180
+ /// nothing punchable to advertise (see nat_status.h).
181
+ /// @return whether a session was actually started.
182
+ bool punch(const PeerId& target) override;
183
+
184
+ /// Sessions currently in flight (diagnostics and tests).
185
+ size_t active_sessions() const;
186
+ /// Punch bursts this node has fired (diagnostics and tests).
187
+ uint64_t punches_started() const noexcept { return punches_started_.load(); }
188
+ /// Messages forwarded on behalf of other peers (diagnostics and tests).
189
+ uint64_t relayed() const noexcept { return relayed_.load(); }
190
+
191
+ private:
192
+ enum class Phase : uint8_t {
193
+ AwaitingPeerConnect, ///< initiator: our Connect is out, waiting for theirs
194
+ AwaitingSync, ///< responder: our Connect is out, waiting for Sync
195
+ Punching, ///< the burst is in flight
196
+ };
197
+
198
+ struct Session {
199
+ bool initiator = false;
200
+ Phase phase = Phase::AwaitingPeerConnect;
201
+ int attempt = 0;
202
+ std::vector<Address> peer_addresses;
203
+ std::chrono::steady_clock::time_point deadline{};
204
+ /// The relay that last carried something from the target, if any. Once one
205
+ /// hop is known to work, the rest of the exchange goes back the same way —
206
+ /// it is the one path proven to reach the target, and it keeps the fan-out
207
+ /// (and the load on peers that cannot help) to the first message alone.
208
+ PeerId via{};
209
+ bool have_via = false;
210
+ };
211
+
212
+ // — message handling (reactor threads) —
213
+ void handle(const Peer& from, ByteView payload);
214
+ void handle_relay_request(const Peer& from, const PeerId& dst, ByteView inner);
215
+ void handle_relayed(const Peer& via, const PeerId& src, ByteView inner);
216
+ /// @param opening whether the sender is opening a rendezvous (rather than
217
+ /// answering ours) — see the `role` byte in the wire format above.
218
+ void handle_connect(const Peer& via, const PeerId& src, bool opening,
219
+ std::vector<Address> addresses);
220
+ void handle_sync(const PeerId& src);
221
+
222
+ // — outgoing —
223
+ /// Wrap `inner` in a Relay envelope and hand it to peers that might reach `dst`:
224
+ /// just `via` when a working hop is already known, otherwise up to max_relays of
225
+ /// our peers. Returns how many were given it.
226
+ size_t relay_to(const PeerId& dst, const Bytes& inner, const PeerId* via);
227
+ bool send_connect(const PeerId& target, bool opening, const PeerId* via);
228
+ void send_sync(const PeerId& target, const PeerId* via);
229
+ /// Fire the dial burst at everything the peer advertised. Caller must NOT hold
230
+ /// the mutex: dialing reaches the reactor, which may run callbacks inline.
231
+ void fire_punch(const PeerId& target, const std::vector<Address>& addresses);
232
+
233
+ // — worker —
234
+ void loop();
235
+ void service_sessions();
236
+
237
+ // — helpers —
238
+ std::vector<Address> own_punch_addresses() const;
239
+ bool relay_budget_ok(const PeerId& from);
240
+ bool in_cooldown(const PeerId& target) const; ///< caller holds mutex_
241
+ void begin_cooldown(const PeerId& target); ///< caller holds mutex_
242
+
243
+ Config config_;
244
+ PeerNetwork* network_ = nullptr;
245
+ DialService* dialer_ = nullptr;
246
+ ExternalAddressService* external_ = nullptr;
247
+
248
+ std::atomic<bool> running_{false};
249
+ std::atomic<uint64_t> punches_started_{0};
250
+ std::atomic<uint64_t> relayed_{0};
251
+
252
+ mutable std::mutex mutex_;
253
+ std::condition_variable cv_;
254
+ std::thread worker_;
255
+ std::unordered_map<PeerId, Session, PeerId::Hash> sessions_;
256
+ std::unordered_map<PeerId, std::chrono::steady_clock::time_point, PeerId::Hash> cooldown_;
257
+
258
+ struct RelayBudget {
259
+ std::chrono::steady_clock::time_point window_started{};
260
+ size_t spent = 0;
261
+ };
262
+ std::mutex relay_mutex_;
263
+ std::unordered_map<PeerId, RelayBudget, PeerId::Hash> relay_budget_;
264
+ };
265
+
266
+ } // 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