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,762 @@
1
+ #include "librats/bindings/rats.h"
2
+
3
+ #include "librats/node/node.h"
4
+ #include "librats/peer/peer_info.h"
5
+ #include "librats/subsystems/dht_discovery.h"
6
+ #include "librats/subsystems/mdns_discovery.h"
7
+ #include "librats/subsystems/port_mapping_service.h"
8
+ #include "librats/subsystems/hole_punch.h"
9
+ #include "librats/subsystems/relay.h"
10
+ #include "librats/subsystems/pubsub.h"
11
+ #include "librats/subsystems/message_json.h"
12
+ #include "librats/subsystems/file_transfer.h"
13
+ #include "librats/subsystems/ping_service.h"
14
+ #include "librats/subsystems/reconnection.h"
15
+ #ifdef RATS_SEARCH_FEATURES
16
+ #include "librats/subsystems/bittorrent.h"
17
+ #endif
18
+ #include "librats/core/address.h"
19
+ #include "librats/util/network_utils.h"
20
+ #include "librats/util/logger.h"
21
+ #include "librats/util/json.h"
22
+ #include "librats/util/version.h"
23
+
24
+ #include <cstdlib>
25
+ #include <cstring>
26
+ #include <memory>
27
+ #include <string>
28
+
29
+ using namespace librats;
30
+
31
+ namespace {
32
+
33
+ // A C handle is a thin owner of the Node plus non-owning pointers to the
34
+ // subsystems it has enabled (the Node owns those). Subsystems are explicit: each
35
+ // rats_enable_* attaches one before start() and records it here; the *_enabled
36
+ // flags dedup the enables that don't keep a pointer. `started` gates the
37
+ // before-start contract so enables after start() can be rejected cleanly.
38
+ struct RatsHandle {
39
+ std::unique_ptr<Node> node;
40
+ PubSub* pubsub = nullptr;
41
+ MessageJson* messages = nullptr;
42
+ FileTransfer* files = nullptr;
43
+ PingService* ping = nullptr;
44
+ ReconnectionService* reconnect = nullptr;
45
+ HolePunch* punch = nullptr;
46
+ Relay* relay = nullptr;
47
+ #ifdef RATS_SEARCH_FEATURES
48
+ Bittorrent* bittorrent = nullptr;
49
+ #endif
50
+ std::string data_dir; ///< copied from NodeConfig so subsystems can co-locate state
51
+ bool dht_enabled = false;
52
+ bool mdns_enabled = false;
53
+ bool portmap_enabled = false;
54
+ bool started = false;
55
+ };
56
+
57
+ // Build a handle around a freshly-constructed Node, remembering the data_dir
58
+ // (the config is moved into the Node, so copy it out first) for subsystems that
59
+ // co-locate persistent state (DHT routing table, reconnection store).
60
+ RatsHandle* make_handle(NodeConfig config) {
61
+ auto* h = new RatsHandle();
62
+ h->data_dir = config.data_dir;
63
+ h->node = std::make_unique<Node>(std::move(config));
64
+ return h;
65
+ }
66
+
67
+ RatsHandle* as_handle(rats_t handle) { return static_cast<RatsHandle*>(handle); }
68
+
69
+ // Turn a caller-supplied (host, port) into a numeric Address. A numeric literal is
70
+ // used verbatim; a hostname is resolved here at the API boundary (IPv4 first, then
71
+ // IPv6) so the reconnection subsystem only ever holds resolved endpoints. Returns
72
+ // nullopt if the host is neither a valid literal nor resolvable.
73
+ std::optional<Address> resolve_target(const char* host, uint16_t port) {
74
+ if (auto ip = IpAddress::parse(host)) return Address{*ip, port};
75
+ std::string resolved = network_utils::resolve_hostname(host);
76
+ if (resolved.empty()) resolved = network_utils::resolve_hostname_v6(host);
77
+ if (auto ip = IpAddress::parse(resolved)) return Address{*ip, port};
78
+ return std::nullopt;
79
+ }
80
+ Node* node_of(rats_t handle) { return as_handle(handle)->node.get(); }
81
+
82
+ char* dup_string(const std::string& s) {
83
+ char* out = static_cast<char*>(std::malloc(s.size() + 1));
84
+ if (out) std::memcpy(out, s.c_str(), s.size() + 1);
85
+ return out;
86
+ }
87
+
88
+ } // namespace
89
+
90
+ extern "C" {
91
+
92
+ const char* rats_error_str(rats_error_t err) {
93
+ switch (err) {
94
+ case RATS_OK: return "RATS_OK";
95
+ case RATS_ERR_INVALID_ARG: return "RATS_ERR_INVALID_ARG";
96
+ case RATS_ERR_NOT_STARTED: return "RATS_ERR_NOT_STARTED";
97
+ case RATS_ERR_ALREADY_STARTED: return "RATS_ERR_ALREADY_STARTED";
98
+ case RATS_ERR_NOT_ENABLED: return "RATS_ERR_NOT_ENABLED";
99
+ case RATS_ERR_NO_SUCH_PEER: return "RATS_ERR_NO_SUCH_PEER";
100
+ case RATS_ERR_BIND: return "RATS_ERR_BIND";
101
+ case RATS_ERR_INTERNAL: return "RATS_ERR_INTERNAL";
102
+ }
103
+ return "RATS_ERR_UNKNOWN";
104
+ }
105
+
106
+ /* — construction / lifecycle — */
107
+
108
+ rats_config_t rats_config_default(void) {
109
+ rats_config_t c;
110
+ c.listen_port = 0;
111
+ c.enable_listen = 1;
112
+ c.bind_address = nullptr;
113
+ c.security = RATS_SECURITY_NOISE;
114
+ c.data_dir = nullptr;
115
+ c.protocol = nullptr;
116
+ c.max_peers = 0;
117
+ c.enable_tcp = 1;
118
+ c.enable_udp = 1;
119
+ c.preferred_transport = RATS_TRANSPORT_UDP;
120
+ c.transport_fallback_ms = 1200;
121
+ return c;
122
+ }
123
+
124
+ rats_t rats_create_config(const rats_config_t* cfg) {
125
+ NodeConfig config;
126
+ if (cfg) {
127
+ config.listen_port = cfg->listen_port;
128
+ config.enable_listen = cfg->enable_listen != 0;
129
+ if (cfg->bind_address) config.bind_address = cfg->bind_address;
130
+ config.security = (cfg->security == RATS_SECURITY_PLAINTEXT) ? NodeConfig::Security::Plaintext
131
+ : NodeConfig::Security::Noise;
132
+ if (cfg->data_dir) config.data_dir = cfg->data_dir;
133
+ if (cfg->protocol) config.protocol = cfg->protocol;
134
+ config.max_peers = cfg->max_peers;
135
+
136
+ // Neither transport enabled is not a configuration anyone means — it is a
137
+ // node that can neither dial nor accept, and it fails *silently*: with
138
+ // enable_listen set, start() refuses; without it, the node comes up and
139
+ // then simply never connects to anything. And it is precisely what the
140
+ // commonest way of filling this struct in C produces, because zeroed
141
+ // memory reads as "both off":
142
+ //
143
+ // rats_config_t cfg = {0}; // or a partial designated initialiser
144
+ // cfg.listen_port = 9000;
145
+ //
146
+ // That worked before these two fields existed, so treating 0/0 as "both"
147
+ // is what keeps it working. Anyone who really wants one transport says so
148
+ // by enabling the other.
149
+ const bool any_transport = cfg->enable_tcp != 0 || cfg->enable_udp != 0;
150
+ config.enable_tcp = any_transport ? (cfg->enable_tcp != 0) : true;
151
+ config.enable_udp = any_transport ? (cfg->enable_udp != 0) : true;
152
+ config.preferred_transport = (cfg->preferred_transport == RATS_TRANSPORT_TCP)
153
+ ? TransportKind::Tcp
154
+ : TransportKind::Udp;
155
+ config.transport_fallback_ms = cfg->transport_fallback_ms;
156
+ }
157
+ return make_handle(std::move(config));
158
+ }
159
+
160
+ rats_t rats_create(uint16_t listen_port) {
161
+ NodeConfig config;
162
+ config.listen_port = listen_port;
163
+ return make_handle(std::move(config));
164
+ }
165
+
166
+ rats_t rats_create_ex(uint16_t listen_port, int enable_listen,
167
+ const char* bind_address, rats_security_t security) {
168
+ NodeConfig config;
169
+ config.listen_port = listen_port;
170
+ config.enable_listen = enable_listen != 0;
171
+ if (bind_address) config.bind_address = bind_address;
172
+ config.security = (security == RATS_SECURITY_PLAINTEXT) ? NodeConfig::Security::Plaintext
173
+ : NodeConfig::Security::Noise;
174
+ return make_handle(std::move(config));
175
+ }
176
+
177
+ void rats_destroy(rats_t node) { delete as_handle(node); }
178
+
179
+ rats_error_t rats_start(rats_t node) {
180
+ auto* h = as_handle(node);
181
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
182
+ if (!h->node->start()) return RATS_ERR_BIND;
183
+ h->started = true;
184
+ return RATS_OK;
185
+ }
186
+
187
+ void rats_stop(rats_t node) {
188
+ auto* h = as_handle(node);
189
+ h->node->stop();
190
+ h->started = false; // allow a subsequent rats_start() to bind again
191
+ }
192
+
193
+ uint16_t rats_listen_port(rats_t node) { return node_of(node)->listen_port(); }
194
+
195
+ uint32_t rats_transports(rats_t node) { return node_of(node)->transports(); }
196
+
197
+ char* rats_local_id(rats_t node) { return dup_string(node_of(node)->local_id().to_hex()); }
198
+
199
+ /* — connections — */
200
+
201
+ rats_error_t rats_connect(rats_t node, const char* host, uint16_t port) {
202
+ if (!host) return RATS_ERR_INVALID_ARG;
203
+ node_of(node)->connect(std::string(host), port);
204
+ return RATS_OK;
205
+ }
206
+
207
+ size_t rats_peer_count(rats_t node) { return node_of(node)->peer_count(); }
208
+
209
+ void rats_set_max_peers(rats_t node, size_t max_peers) {
210
+ node_of(node)->set_max_peers(max_peers);
211
+ }
212
+
213
+ size_t rats_max_peers(rats_t node) { return node_of(node)->max_peers(); }
214
+
215
+ /* — messaging — */
216
+
217
+ rats_error_t rats_send(rats_t node, const char* peer_id_hex,
218
+ const char* channel, const void* data, size_t len) {
219
+ if (!peer_id_hex || !channel) return RATS_ERR_INVALID_ARG;
220
+ auto id = PeerId::from_hex(peer_id_hex);
221
+ if (!id) return RATS_ERR_INVALID_ARG;
222
+ if (!node_of(node)->peer(*id)) return RATS_ERR_NO_SUCH_PEER;
223
+ node_of(node)->send(*id, channel, ByteView(static_cast<const uint8_t*>(data), len));
224
+ return RATS_OK;
225
+ }
226
+
227
+ rats_error_t rats_broadcast(rats_t node, const char* channel, const void* data, size_t len) {
228
+ if (!channel) return RATS_ERR_INVALID_ARG;
229
+ node_of(node)->broadcast(channel, ByteView(static_cast<const uint8_t*>(data), len));
230
+ return RATS_OK;
231
+ }
232
+
233
+ rats_error_t rats_on_peer_connected(rats_t node, rats_peer_cb cb, void* user) {
234
+ if (!cb) return RATS_ERR_INVALID_ARG;
235
+ node_of(node)->on_peer_connected([cb, user](const Peer& peer) {
236
+ cb(user, peer.id().to_hex().c_str());
237
+ });
238
+ return RATS_OK;
239
+ }
240
+
241
+ rats_error_t rats_on_peer_disconnected(rats_t node, rats_peer_cb cb, void* user) {
242
+ if (!cb) return RATS_ERR_INVALID_ARG;
243
+ node_of(node)->on_peer_disconnected([cb, user](const PeerId& id) {
244
+ cb(user, id.to_hex().c_str());
245
+ });
246
+ return RATS_OK;
247
+ }
248
+
249
+ rats_error_t rats_on(rats_t node, const char* channel, rats_message_cb cb, void* user) {
250
+ if (!channel || !cb) return RATS_ERR_INVALID_ARG;
251
+ node_of(node)->on(channel, [cb, user](const Peer& peer, ByteView data) {
252
+ cb(user, peer.id().to_hex().c_str(), data.data(), data.size());
253
+ });
254
+ return RATS_OK;
255
+ }
256
+
257
+ /* — discovery / NAT subsystems — */
258
+
259
+ rats_error_t rats_enable_dht(rats_t node, uint16_t dht_port, const char* discovery_key) {
260
+ auto* h = as_handle(node);
261
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
262
+ if (h->dht_enabled) return RATS_OK;
263
+ DhtDiscovery::Config config;
264
+ config.dht_port = dht_port;
265
+ config.data_dir = h->data_dir; // co-locate routing tables with identity (else cwd)
266
+ if (discovery_key) config.discovery_key = discovery_key;
267
+ h->node->add_subsystem(std::make_unique<DhtDiscovery>(std::move(config)));
268
+ h->dht_enabled = true;
269
+ return RATS_OK;
270
+ }
271
+
272
+ rats_error_t rats_enable_mdns(rats_t node) {
273
+ auto* h = as_handle(node);
274
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
275
+ if (h->mdns_enabled) return RATS_OK;
276
+ h->node->add_subsystem(std::make_unique<MdnsDiscovery>());
277
+ h->mdns_enabled = true;
278
+ return RATS_OK;
279
+ }
280
+
281
+ rats_error_t rats_enable_port_mapping(rats_t node, int enable_upnp, int enable_natpmp) {
282
+ auto* h = as_handle(node);
283
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
284
+ if (h->portmap_enabled) return RATS_OK;
285
+ PortMappingConfig config;
286
+ config.enable_upnp = enable_upnp != 0;
287
+ config.enable_natpmp = enable_natpmp != 0;
288
+ h->node->add_subsystem(std::make_unique<PortMappingService>(config));
289
+ h->portmap_enabled = true;
290
+ return RATS_OK;
291
+ }
292
+
293
+ rats_error_t rats_enable_hole_punch(rats_t node, int serve_as_relay) {
294
+ auto* h = as_handle(node);
295
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
296
+ if (!h->punch) {
297
+ HolePunch::Config config;
298
+ config.enable_relay = serve_as_relay != 0;
299
+ h->punch = h->node->add_subsystem(std::make_unique<HolePunch>(config));
300
+ }
301
+ return RATS_OK;
302
+ }
303
+
304
+ rats_error_t rats_enable_relay(rats_t node, int serve_as_relay) {
305
+ auto* h = as_handle(node);
306
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
307
+ if (!h->relay) {
308
+ Relay::Config config;
309
+ config.serve = serve_as_relay != 0;
310
+ h->relay = h->node->add_subsystem(std::make_unique<Relay>(config));
311
+ }
312
+ return RATS_OK;
313
+ }
314
+
315
+ rats_error_t rats_connect_via_relay(rats_t node, const char* peer_id_hex) {
316
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
317
+ auto* h = as_handle(node);
318
+ if (!h->relay) return RATS_ERR_NOT_ENABLED;
319
+ auto id = PeerId::from_hex(peer_id_hex);
320
+ if (!id) return RATS_ERR_INVALID_ARG;
321
+ // As with punching, every reason an attempt is declined is "nothing to do or
322
+ // nothing to try with" — already connected, already trying, in cooldown, or no
323
+ // peer that could carry it — and none of them is separately actionable.
324
+ return h->relay->connect_via_relay(*id) ? RATS_OK : RATS_ERR_NO_SUCH_PEER;
325
+ }
326
+
327
+ rats_error_t rats_punch_peer(rats_t node, const char* peer_id_hex) {
328
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
329
+ auto* h = as_handle(node);
330
+ if (!h->punch) return RATS_ERR_NOT_ENABLED;
331
+ auto id = PeerId::from_hex(peer_id_hex);
332
+ if (!id) return RATS_ERR_INVALID_ARG;
333
+ // Every reason a punch is declined is "there is nothing to do or nothing to try
334
+ // with" — already connected, already punching, in cooldown, or no external
335
+ // endpoint learned yet. None of them is an error the caller can act on
336
+ // differently, so they collapse into one answer.
337
+ return h->punch->punch(*id) ? RATS_OK : RATS_ERR_NO_SUCH_PEER;
338
+ }
339
+
340
+ int rats_nat_mapping(rats_t node) {
341
+ switch (node_of(node)->nat_status().udp_mapping()) {
342
+ case NatMapping::Open: return RATS_NAT_OPEN;
343
+ case NatMapping::EndpointIndependent: return RATS_NAT_ENDPOINT_INDEPENDENT;
344
+ case NatMapping::EndpointDependent: return RATS_NAT_ENDPOINT_DEPENDENT;
345
+ case NatMapping::Unknown: break;
346
+ }
347
+ return RATS_NAT_UNKNOWN;
348
+ }
349
+
350
+ /* — peer enumeration — */
351
+
352
+ char** rats_peer_ids(rats_t node, size_t* count) {
353
+ auto infos = node_of(node)->peers();
354
+ if (count) *count = infos.size();
355
+ if (infos.empty()) return nullptr;
356
+ char** ids = static_cast<char**>(std::malloc(infos.size() * sizeof(char*)));
357
+ if (!ids) { if (count) *count = 0; return nullptr; }
358
+ for (size_t i = 0; i < infos.size(); ++i) ids[i] = dup_string(infos[i].id.to_hex());
359
+ return ids;
360
+ }
361
+
362
+ int rats_peer_transport(rats_t node, const char* peer_id_hex) {
363
+ if (!peer_id_hex) return -1;
364
+ auto id = PeerId::from_hex(peer_id_hex);
365
+ if (!id) return -1;
366
+ for (const PeerInfo& info : node_of(node)->peers()) {
367
+ if (info.id != *id) continue;
368
+ switch (info.transport) {
369
+ case TransportKind::Udp: return RATS_TRANSPORT_UDP;
370
+ case TransportKind::Relay: return RATS_TRANSPORT_RELAY;
371
+ case TransportKind::Tcp: break;
372
+ }
373
+ return RATS_TRANSPORT_TCP;
374
+ }
375
+ return -1;
376
+ }
377
+
378
+ int rats_peer_transports(rats_t node, const char* peer_id_hex) {
379
+ if (!peer_id_hex) return -1;
380
+ auto id = PeerId::from_hex(peer_id_hex);
381
+ if (!id) return -1;
382
+ for (const PeerInfo& info : node_of(node)->peers())
383
+ if (info.id == *id) return static_cast<int>(info.supported_transports);
384
+ return -1;
385
+ }
386
+
387
+ void rats_free_peer_ids(char** ids, size_t count) {
388
+ if (!ids) return;
389
+ for (size_t i = 0; i < count; ++i) std::free(ids[i]);
390
+ std::free(ids);
391
+ }
392
+
393
+ /* — pub/sub — */
394
+
395
+ rats_error_t rats_enable_pubsub(rats_t node) {
396
+ auto* h = as_handle(node);
397
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
398
+ if (!h->pubsub) h->pubsub = h->node->add_subsystem(std::make_unique<PubSub>());
399
+ return RATS_OK;
400
+ }
401
+
402
+ rats_error_t rats_subscribe(rats_t node, const char* topic, rats_topic_cb cb, void* user) {
403
+ if (!topic || !cb) return RATS_ERR_INVALID_ARG;
404
+ auto* h = as_handle(node);
405
+ if (!h->pubsub) return RATS_ERR_NOT_ENABLED;
406
+ h->pubsub->subscribe(topic,
407
+ [cb, user](const PeerId& from, const std::string& t, ByteView data) {
408
+ cb(user, from.to_hex().c_str(), t.c_str(), data.data(), data.size());
409
+ });
410
+ return RATS_OK;
411
+ }
412
+
413
+ rats_error_t rats_unsubscribe(rats_t node, const char* topic) {
414
+ if (!topic) return RATS_ERR_INVALID_ARG;
415
+ auto* h = as_handle(node);
416
+ if (!h->pubsub) return RATS_ERR_NOT_ENABLED;
417
+ h->pubsub->unsubscribe(topic);
418
+ return RATS_OK;
419
+ }
420
+
421
+ rats_error_t rats_publish(rats_t node, const char* topic, const void* data, size_t len) {
422
+ if (!topic) return RATS_ERR_INVALID_ARG;
423
+ auto* h = as_handle(node);
424
+ if (!h->pubsub) return RATS_ERR_NOT_ENABLED;
425
+ h->pubsub->publish(topic, ByteView(static_cast<const uint8_t*>(data), len));
426
+ return RATS_OK;
427
+ }
428
+
429
+ /* — typed JSON messaging — */
430
+
431
+ rats_error_t rats_enable_json(rats_t node) {
432
+ auto* h = as_handle(node);
433
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
434
+ if (!h->messages) h->messages = h->node->add_subsystem(std::make_unique<MessageJson>());
435
+ return RATS_OK;
436
+ }
437
+
438
+ rats_error_t rats_on_json(rats_t node, const char* type, rats_json_cb cb, void* user) {
439
+ if (!type || !cb) return RATS_ERR_INVALID_ARG;
440
+ auto* h = as_handle(node);
441
+ if (!h->messages) return RATS_ERR_NOT_ENABLED;
442
+ h->messages->on(type, [cb, user](const PeerId& from, const librats::Json& data) {
443
+ cb(user, from.to_hex().c_str(), data.dump().c_str());
444
+ });
445
+ return RATS_OK;
446
+ }
447
+
448
+ rats_error_t rats_once_json(rats_t node, const char* type, rats_json_cb cb, void* user) {
449
+ if (!type || !cb) return RATS_ERR_INVALID_ARG;
450
+ auto* h = as_handle(node);
451
+ if (!h->messages) return RATS_ERR_NOT_ENABLED;
452
+ h->messages->once(type, [cb, user](const PeerId& from, const librats::Json& data) {
453
+ cb(user, from.to_hex().c_str(), data.dump().c_str());
454
+ });
455
+ return RATS_OK;
456
+ }
457
+
458
+ rats_error_t rats_off_json(rats_t node, const char* type) {
459
+ if (!type) return RATS_ERR_INVALID_ARG;
460
+ auto* h = as_handle(node);
461
+ if (!h->messages) return RATS_ERR_NOT_ENABLED;
462
+ h->messages->off(type);
463
+ return RATS_OK;
464
+ }
465
+
466
+ rats_error_t rats_send_json(rats_t node, const char* peer_id_hex, const char* type, const char* json) {
467
+ if (!peer_id_hex || !type || !json) return RATS_ERR_INVALID_ARG;
468
+ auto* h = as_handle(node);
469
+ if (!h->messages) return RATS_ERR_NOT_ENABLED;
470
+ auto id = PeerId::from_hex(peer_id_hex);
471
+ if (!id) return RATS_ERR_INVALID_ARG;
472
+ auto j = librats::Json::parse(json, nullptr, /*allow_exceptions=*/false);
473
+ if (j.is_discarded()) return RATS_ERR_INVALID_ARG;
474
+ if (!h->node->peer(*id)) return RATS_ERR_NO_SUCH_PEER;
475
+ h->messages->send(*id, type, j);
476
+ return RATS_OK;
477
+ }
478
+
479
+ rats_error_t rats_broadcast_json(rats_t node, const char* type, const char* json) {
480
+ if (!type || !json) return RATS_ERR_INVALID_ARG;
481
+ auto* h = as_handle(node);
482
+ if (!h->messages) return RATS_ERR_NOT_ENABLED;
483
+ auto j = librats::Json::parse(json, nullptr, /*allow_exceptions=*/false);
484
+ if (j.is_discarded()) return RATS_ERR_INVALID_ARG;
485
+ h->messages->send(type, j);
486
+ return RATS_OK;
487
+ }
488
+
489
+ /* — file transfer — */
490
+
491
+ rats_error_t rats_enable_file_transfer(rats_t node, const char* temp_dir) {
492
+ auto* h = as_handle(node);
493
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
494
+ if (!h->files)
495
+ h->files = h->node->add_subsystem(std::make_unique<FileTransfer>(temp_dir ? temp_dir : "."));
496
+ return RATS_OK;
497
+ }
498
+
499
+ rats_error_t rats_on_file_offer(rats_t node, rats_file_offer_cb cb, void* user) {
500
+ if (!cb) return RATS_ERR_INVALID_ARG;
501
+ auto* h = as_handle(node);
502
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
503
+ h->files->on_offer([cb, user](const FileTransfer::Offer& o) {
504
+ cb(user, o.from.to_hex().c_str(), o.id, o.name.c_str(), o.size, o.is_directory ? 1 : 0);
505
+ });
506
+ return RATS_OK;
507
+ }
508
+
509
+ rats_error_t rats_on_file_progress(rats_t node, rats_file_progress_cb cb, void* user) {
510
+ if (!cb) return RATS_ERR_INVALID_ARG;
511
+ auto* h = as_handle(node);
512
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
513
+ h->files->on_progress([cb, user](const FileTransfer::Progress& p) {
514
+ cb(user, p.id, p.peer.to_hex().c_str(), p.bytes_transferred, p.total_bytes,
515
+ static_cast<int>(p.status));
516
+ });
517
+ return RATS_OK;
518
+ }
519
+
520
+ rats_error_t rats_on_file_complete(rats_t node, rats_file_complete_cb cb, void* user) {
521
+ if (!cb) return RATS_ERR_INVALID_ARG;
522
+ auto* h = as_handle(node);
523
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
524
+ h->files->on_complete([cb, user](uint64_t id, bool success, const std::string& path) {
525
+ cb(user, id, success ? 1 : 0, path.c_str());
526
+ });
527
+ return RATS_OK;
528
+ }
529
+
530
+ uint64_t rats_send_file(rats_t node, const char* peer_id_hex, const char* path) {
531
+ if (!peer_id_hex || !path) return 0;
532
+ auto* h = as_handle(node);
533
+ if (!h->files) return 0;
534
+ auto id = PeerId::from_hex(peer_id_hex);
535
+ if (!id) return 0;
536
+ return h->files->send_file(*id, path);
537
+ }
538
+
539
+ uint64_t rats_send_directory(rats_t node, const char* peer_id_hex, const char* dir_path) {
540
+ if (!peer_id_hex || !dir_path) return 0;
541
+ auto* h = as_handle(node);
542
+ if (!h->files) return 0;
543
+ auto id = PeerId::from_hex(peer_id_hex);
544
+ if (!id) return 0;
545
+ return h->files->send_directory(*id, dir_path);
546
+ }
547
+
548
+ rats_error_t rats_accept_file(rats_t node, const char* peer_id_hex, uint64_t transfer_id,
549
+ const char* dest_path) {
550
+ if (!peer_id_hex || !dest_path) return RATS_ERR_INVALID_ARG;
551
+ auto* h = as_handle(node);
552
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
553
+ auto id = PeerId::from_hex(peer_id_hex);
554
+ if (!id) return RATS_ERR_INVALID_ARG;
555
+ h->files->accept(*id, transfer_id, dest_path);
556
+ return RATS_OK;
557
+ }
558
+
559
+ rats_error_t rats_reject_file(rats_t node, const char* peer_id_hex, uint64_t transfer_id) {
560
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
561
+ auto* h = as_handle(node);
562
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
563
+ auto id = PeerId::from_hex(peer_id_hex);
564
+ if (!id) return RATS_ERR_INVALID_ARG;
565
+ h->files->reject(*id, transfer_id);
566
+ return RATS_OK;
567
+ }
568
+
569
+ rats_error_t rats_cancel_file(rats_t node, const char* peer_id_hex, uint64_t transfer_id) {
570
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
571
+ auto* h = as_handle(node);
572
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
573
+ auto id = PeerId::from_hex(peer_id_hex);
574
+ if (!id) return RATS_ERR_INVALID_ARG;
575
+ return h->files->cancel(*id, transfer_id) ? RATS_OK : RATS_ERR_NO_SUCH_PEER;
576
+ }
577
+
578
+ rats_error_t rats_pause_file(rats_t node, const char* peer_id_hex, uint64_t transfer_id) {
579
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
580
+ auto* h = as_handle(node);
581
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
582
+ auto id = PeerId::from_hex(peer_id_hex);
583
+ if (!id) return RATS_ERR_INVALID_ARG;
584
+ return h->files->pause(*id, transfer_id) ? RATS_OK : RATS_ERR_NO_SUCH_PEER;
585
+ }
586
+
587
+ rats_error_t rats_resume_file(rats_t node, const char* peer_id_hex, uint64_t transfer_id) {
588
+ if (!peer_id_hex) return RATS_ERR_INVALID_ARG;
589
+ auto* h = as_handle(node);
590
+ if (!h->files) return RATS_ERR_NOT_ENABLED;
591
+ auto id = PeerId::from_hex(peer_id_hex);
592
+ if (!id) return RATS_ERR_INVALID_ARG;
593
+ return h->files->resume(*id, transfer_id) ? RATS_OK : RATS_ERR_NO_SUCH_PEER;
594
+ }
595
+
596
+ /* — liveness — */
597
+
598
+ rats_error_t rats_enable_ping(rats_t node) {
599
+ auto* h = as_handle(node);
600
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
601
+ if (!h->ping) h->ping = h->node->add_subsystem(std::make_unique<PingService>());
602
+ return RATS_OK;
603
+ }
604
+
605
+ int64_t rats_peer_rtt_ms(rats_t node, const char* peer_id_hex) {
606
+ if (!peer_id_hex) return -1;
607
+ auto* p = as_handle(node)->ping;
608
+ if (!p) return -1;
609
+ auto id = PeerId::from_hex(peer_id_hex);
610
+ if (!id) return -1;
611
+ auto rtt = p->last_rtt(*id);
612
+ return rtt ? static_cast<int64_t>(rtt->count()) : -1;
613
+ }
614
+
615
+ /* — automatic reconnection — */
616
+
617
+ rats_error_t rats_enable_reconnect(rats_t node) {
618
+ auto* h = as_handle(node);
619
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
620
+ if (!h->reconnect) {
621
+ ReconnectionService::Config rc;
622
+ if (!h->data_dir.empty()) rc.store_path = h->data_dir + "/peers.json";
623
+ h->reconnect = h->node->add_subsystem(std::make_unique<ReconnectionService>(rc));
624
+ }
625
+ return RATS_OK;
626
+ }
627
+
628
+ /* — BitTorrent (only functional when built with RATS_SEARCH_FEATURES) — */
629
+
630
+ rats_error_t rats_enable_bittorrent(rats_t node, uint16_t listen_port, const char* download_path) {
631
+ #ifdef RATS_SEARCH_FEATURES
632
+ auto* h = as_handle(node);
633
+ if (h->started) return RATS_ERR_ALREADY_STARTED;
634
+ if (!h->bittorrent) {
635
+ Bittorrent::Config cfg;
636
+ cfg.client.listen_port = listen_port;
637
+ cfg.client.download_path = download_path ? download_path : ".";
638
+ h->bittorrent = h->node->add_subsystem(std::make_unique<Bittorrent>(cfg));
639
+ }
640
+ return RATS_OK;
641
+ #else
642
+ (void)node; (void)listen_port; (void)download_path;
643
+ return RATS_ERR_NOT_ENABLED;
644
+ #endif
645
+ }
646
+
647
+ rats_error_t rats_bt_add_magnet(rats_t node, const char* magnet_uri, const char* save_path) {
648
+ #ifdef RATS_SEARCH_FEATURES
649
+ if (!magnet_uri) return RATS_ERR_INVALID_ARG;
650
+ auto* h = as_handle(node);
651
+ if (!h->bittorrent || !h->bittorrent->is_running()) return RATS_ERR_NOT_ENABLED;
652
+ auto* c = h->bittorrent->client();
653
+ // Mutating the client must happen on its reactor thread.
654
+ c->reactor().post([c, uri = std::string(magnet_uri),
655
+ sp = std::string(save_path ? save_path : "")] { c->add_magnet(uri, sp); });
656
+ return RATS_OK;
657
+ #else
658
+ (void)node; (void)magnet_uri; (void)save_path;
659
+ return RATS_ERR_NOT_ENABLED;
660
+ #endif
661
+ }
662
+
663
+ rats_error_t rats_bt_add_torrent_file(rats_t node, const char* torrent_path, const char* save_path) {
664
+ #ifdef RATS_SEARCH_FEATURES
665
+ if (!torrent_path) return RATS_ERR_INVALID_ARG;
666
+ auto* h = as_handle(node);
667
+ if (!h->bittorrent || !h->bittorrent->is_running()) return RATS_ERR_NOT_ENABLED;
668
+ auto info = bittorrent::TorrentInfo::from_file(torrent_path);
669
+ if (!info) return RATS_ERR_INVALID_ARG;
670
+ auto* c = h->bittorrent->client();
671
+ c->reactor().post([c, info = *info, sp = std::string(save_path ? save_path : "")] {
672
+ c->add_torrent(info, sp);
673
+ });
674
+ return RATS_OK;
675
+ #else
676
+ (void)node; (void)torrent_path; (void)save_path;
677
+ return RATS_ERR_NOT_ENABLED;
678
+ #endif
679
+ }
680
+
681
+ rats_error_t rats_bt_remove_torrent(rats_t node, const char* info_hash_hex) {
682
+ #ifdef RATS_SEARCH_FEATURES
683
+ if (!info_hash_hex) return RATS_ERR_INVALID_ARG;
684
+ auto* h = as_handle(node);
685
+ if (!h->bittorrent || !h->bittorrent->is_running()) return RATS_ERR_NOT_ENABLED;
686
+ auto ih = bittorrent::info_hash_from_hex(info_hash_hex);
687
+ if (!ih) return RATS_ERR_INVALID_ARG;
688
+ auto* c = h->bittorrent->client();
689
+ c->reactor().post([c, ih = *ih] { c->remove_torrent(ih); });
690
+ return RATS_OK;
691
+ #else
692
+ (void)node; (void)info_hash_hex;
693
+ return RATS_ERR_NOT_ENABLED;
694
+ #endif
695
+ }
696
+
697
+ rats_error_t rats_add_reconnect(rats_t node, const char* host, uint16_t port) {
698
+ if (!host) return RATS_ERR_INVALID_ARG;
699
+ auto* h = as_handle(node);
700
+ if (!h->reconnect) return RATS_ERR_NOT_ENABLED;
701
+ const auto target = resolve_target(host, port);
702
+ if (!target) return RATS_ERR_INVALID_ARG; // not a valid literal, and did not resolve
703
+ h->reconnect->add(*target);
704
+ return RATS_OK;
705
+ }
706
+
707
+ rats_error_t rats_remove_reconnect(rats_t node, const char* host, uint16_t port) {
708
+ if (!host) return RATS_ERR_INVALID_ARG;
709
+ auto* h = as_handle(node);
710
+ if (!h->reconnect) return RATS_ERR_NOT_ENABLED;
711
+ const auto target = resolve_target(host, port);
712
+ if (!target) return RATS_ERR_INVALID_ARG;
713
+ h->reconnect->remove(*target);
714
+ return RATS_OK;
715
+ }
716
+
717
+ /* — logging — */
718
+
719
+ void rats_set_log_level(rats_log_level_t level) {
720
+ Logger::getInstance().set_log_level(static_cast<LogLevel>(level));
721
+ }
722
+
723
+ void rats_set_log_file(const char* path) {
724
+ auto& logger = Logger::getInstance();
725
+ if (path && path[0] != '\0') {
726
+ logger.set_log_file_path(path);
727
+ logger.set_file_logging_enabled(true);
728
+ } else {
729
+ logger.set_file_logging_enabled(false);
730
+ }
731
+ }
732
+
733
+ /* — protocol identity (node-scoped) — */
734
+
735
+ char* rats_protocol(rats_t node) {
736
+ return dup_string(node_of(node)->protocol());
737
+ }
738
+
739
+ /* — library info (process-global) — */
740
+
741
+ const char* rats_version_string(void) { return version::STRING; }
742
+
743
+ void rats_version(int* major, int* minor, int* patch, int* build) {
744
+ if (major) *major = version::MAJOR;
745
+ if (minor) *minor = version::MINOR;
746
+ if (patch) *patch = version::PATCH;
747
+ if (build) *build = version::BUILD;
748
+ }
749
+
750
+ const char* rats_git_describe(void) { return version::GIT_DESCRIBE; }
751
+
752
+ uint32_t rats_abi(void) {
753
+ return (static_cast<uint32_t>(version::MAJOR) << 16) |
754
+ (static_cast<uint32_t>(version::MINOR) << 8) |
755
+ (static_cast<uint32_t>(version::PATCH));
756
+ }
757
+
758
+ /* — utility — */
759
+
760
+ void rats_string_free(char* str) { std::free(str); }
761
+
762
+ } // extern "C"