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