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,501 @@
1
+ #include "librats/dht/dht.h"
2
+ #include "librats/dht/node.h"
3
+ #include "librats/dht/udp_transport.h"
4
+ #include "librats/dht/dht_runner.h"
5
+ #include "librats/dht/persistence.h"
6
+ #include "librats/dht/bep42.h"
7
+ #include "librats/dht/log.h"
8
+ #include "librats/util/network_utils.h"
9
+ #include "librats/util/fs.h"
10
+
11
+ #include <atomic>
12
+ #include <future>
13
+ #include <random>
14
+ #include <shared_mutex>
15
+ #include <utility>
16
+
17
+ namespace librats {
18
+
19
+ namespace {
20
+ // How often the running node flushes its routing table to disk, so a crash loses at
21
+ // most this much of the warm contact set (matches the old maintenance-loop cadence).
22
+ constexpr std::chrono::minutes kAutosaveInterval{5};
23
+
24
+ // Serialize a routing-table snapshot to `path`, creating the data dir first. Shared by
25
+ // the explicit save_routing_table() call and the periodic autosave.
26
+ bool write_routing_table(const std::string& path, const std::string& data_dir,
27
+ const NodeId& self, const std::vector<dht::NodeEntry>& contacts) {
28
+ if (!data_dir.empty() && data_dir != ".")
29
+ create_directories(data_dir.c_str());
30
+ return dht::save_routing_table(path, self, contacts);
31
+ }
32
+
33
+ // Marks a DHT loop thread with the Impl it belongs to (null on every other thread).
34
+ // Planted from *inside* the thread at start(), so it needs no synchronisation to read
35
+ // and nothing to clear: it dies with the thread it lives on.
36
+ thread_local const void* tls_loop_owner = nullptr;
37
+ } // namespace
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // The facade owns the engine trio (transport + node + runner) and marshals every
41
+ // public call onto the runner's loop thread, where the lock-free Node lives.
42
+ // ---------------------------------------------------------------------------
43
+ struct DhtClient::Impl {
44
+ int port = 0;
45
+ std::string bind_address;
46
+ std::string data_directory;
47
+ AddressFamily family = AddressFamily::IPv4;
48
+ bool ipv6 = false;
49
+ NodeId self{}; // authoritative once the node exists; cached otherwise
50
+ std::string external_address; // cached pre-start; node owns it once running
51
+ std::atomic<bool> running{false};
52
+
53
+ std::unique_ptr<dht::UdpTransport> transport;
54
+ std::unique_ptr<dht::Node> node;
55
+ std::unique_ptr<dht::DhtRunner> runner;
56
+
57
+ /// Guards the *existence* of the engine trio above, which start()/stop() create and
58
+ /// destroy while other threads are calling in. `running` alone cannot do that job: a
59
+ /// caller that reads it as true can be descheduled and resume after stop() has already
60
+ /// freed the runner, so the very next `runner->post()` touches a dead object (and a
61
+ /// getter blocked on its promise would never be woken). start()/stop() take it
62
+ /// exclusively for the whole teardown; every public call takes it shared for its whole
63
+ /// duration — via lock_lifetime(), never directly. Teardown therefore cannot begin
64
+ /// while a call is in flight, which is what lets on_loop() below assume the loop
65
+ /// thread is alive and will answer it.
66
+ mutable std::shared_mutex lifetime;
67
+
68
+ /// True when the caller is our own runner's loop thread — see lock_lifetime(). Reads
69
+ /// the marker start() planted on that thread rather than anything hanging off
70
+ /// `runner`, which is precisely what stop() destroys underneath a caller that has
71
+ /// not taken `lifetime` yet.
72
+ bool on_loop_thread() const { return tls_loop_owner == this; }
73
+
74
+ /// `lifetime` held shared for the caller's whole call — except on the loop thread,
75
+ /// where it is deliberately *not* taken. A user callback runs there (peer discovery,
76
+ /// the periodic autosave) and may legitimately call back into the facade; since
77
+ /// stop() takes the lock exclusively and only then joins that very thread, waiting
78
+ /// for it here would deadlock the two against each other forever. Skipping it is
79
+ /// safe for exactly the same reason: if we are executing on the loop, the loop is
80
+ /// alive, so stop() cannot have got past its join() to the resets below it.
81
+ std::shared_lock<std::shared_mutex> lock_lifetime() const {
82
+ if (on_loop_thread()) return {};
83
+ return std::shared_lock<std::shared_mutex>(lifetime);
84
+ }
85
+
86
+ // Run `f` on the loop thread and return its result (used by getters). Safe to block on
87
+ // the promise: callers hold `lifetime` shared, so stop() cannot have started and the
88
+ // loop thread is still there to run the task.
89
+ template <class F>
90
+ auto on_loop(F&& f) -> decltype(f()) {
91
+ // Already on the loop: run it here. Posting would be waiting on ourselves, and
92
+ // `f` touches the same lock-free Node the loop would have touched for us. Only
93
+ // this round-trip path runs inline — the commands below stay post()s, so a
94
+ // callback can never re-enter the Node from inside one of the Node's own
95
+ // callbacks (cancel_lookup erasing the very traversal that is calling us).
96
+ if (on_loop_thread()) return f();
97
+ using R = decltype(f());
98
+ std::promise<R> p;
99
+ auto fut = p.get_future();
100
+ runner->post([&] { p.set_value(f()); }); // cannot fail: `lifetime` is held shared
101
+ return fut.get();
102
+ }
103
+
104
+ static dht::TimePoint now() { return std::chrono::steady_clock::now(); }
105
+ };
106
+
107
+ DhtClient::DhtClient(int port, const std::string& bind_address,
108
+ const std::string& data_directory, AddressFamily address_family)
109
+ : impl_(std::make_unique<Impl>()) {
110
+ impl_->port = port;
111
+ impl_->bind_address = bind_address;
112
+ impl_->data_directory = data_directory;
113
+ impl_->family = address_family;
114
+ impl_->ipv6 = (address_family == AddressFamily::IPv6);
115
+
116
+ std::mt19937 gen(std::random_device{}());
117
+ std::uniform_int_distribution<int> b(0, 255);
118
+ for (auto& x : impl_->self) x = static_cast<uint8_t>(b(gen));
119
+
120
+ LOG_DEBUG("dht", "client created (" << (impl_->ipv6 ? "IPv6" : "IPv4")
121
+ << ", port " << impl_->port << ", data_dir '" << impl_->data_directory << "')");
122
+ }
123
+
124
+ DhtClient::~DhtClient() { stop(); }
125
+
126
+ bool DhtClient::start() {
127
+ std::unique_lock<std::shared_mutex> lock(impl_->lifetime);
128
+ if (impl_->running.load()) return true;
129
+
130
+ const int requested_port = impl_->port;
131
+ impl_->transport = std::make_unique<dht::UdpTransport>(impl_->port, impl_->bind_address, impl_->family);
132
+ if (!impl_->transport->is_open()) {
133
+ LOG_ERROR("dht", "failed to open " << (impl_->ipv6 ? "IPv6" : "IPv4")
134
+ << " UDP socket on port " << requested_port);
135
+ impl_->transport.reset();
136
+ return false;
137
+ }
138
+ impl_->port = impl_->transport->port(); // record the actual bound port
139
+ if (requested_port > 0 && impl_->port != requested_port)
140
+ LOG_WARN("dht", "port " << requested_port << " unavailable, bound ephemeral port " << impl_->port);
141
+
142
+ // Restore our identity + a warm contact set if we've run here before. Only when a
143
+ // data dir is configured: without one nothing is persisted (stop() saves under the
144
+ // same condition), and since the file name is no longer port-unique, blindly loading
145
+ // a cwd file would cross-contaminate unrelated ephemeral nodes sharing a directory.
146
+ std::vector<dht::NodeEntry> contacts;
147
+ if (!impl_->data_directory.empty()) {
148
+ NodeId loaded = impl_->self;
149
+ if (dht::load_routing_table(routing_table_file_path(), loaded, contacts)) {
150
+ impl_->self = loaded;
151
+ LOG_INFO("dht", "restored identity " << dht::short_hex(impl_->self)
152
+ << " and " << contacts.size() << " contact(s) from disk");
153
+ }
154
+ }
155
+
156
+ impl_->node = std::make_unique<dht::Node>(*impl_->transport, impl_->self, impl_->ipv6);
157
+ if (!contacts.empty()) impl_->node->routing_table().load_contacts(contacts);
158
+
159
+ impl_->runner = std::make_unique<dht::DhtRunner>(*impl_->node, *impl_->transport);
160
+
161
+ // Periodically persist the warm contact set so a crash doesn't lose it (we otherwise
162
+ // only save on a clean stop()). Runs on the loop thread, so it reads the lock-free
163
+ // Node directly — no on_loop() round-trip and no lock needed (see lock_lifetime()):
164
+ // stop() joins the loop before it resets the node, so the node is alive for as long
165
+ // as this can run. Only when a data dir is configured, to avoid littering the CWD.
166
+ if (!impl_->data_directory.empty() && impl_->data_directory != ".") {
167
+ impl_->runner->set_periodic(kAutosaveInterval, [this] {
168
+ if (!impl_->node) return;
169
+ const auto contacts = impl_->node->routing_table().good_contacts();
170
+ write_routing_table(routing_table_file_path(), impl_->data_directory,
171
+ impl_->node->self(), contacts);
172
+ LOG_DEBUG("dht", "autosaved " << contacts.size() << " contact(s)");
173
+ });
174
+ }
175
+
176
+ // Mark the loop thread before anything runs on it, so a callback that calls back
177
+ // into the facade is recognised from its very first invocation.
178
+ impl_->runner->start([impl = impl_.get()] { tls_loop_owner = impl; });
179
+ impl_->running.store(true);
180
+ LOG_INFO("dht", "started, node " << dht::short_hex(impl_->self) << ", "
181
+ << (impl_->ipv6 ? "IPv6" : "IPv4") << " on port " << impl_->port);
182
+ return true;
183
+ }
184
+
185
+ void DhtClient::stop() {
186
+ // Exclusive for the whole teardown: no public call may be mid-flight while the trio
187
+ // below is destroyed, and none may start until it is done. See Impl::lifetime. This
188
+ // one method still may not be called *from* the loop thread — it joins that thread,
189
+ // so it would be waiting on itself no matter how the lock behaved.
190
+ std::unique_lock<std::shared_mutex> lock(impl_->lifetime);
191
+ if (!impl_->running.exchange(false)) return;
192
+ LOG_INFO("dht", "stopping");
193
+ if (impl_->runner) impl_->runner->stop(); // join the loop thread first
194
+
195
+ // Single-threaded again: persist (only when a data dir is configured, to avoid
196
+ // littering) directly from the idle node. Written inline rather than through
197
+ // save_routing_table(), which would take `lifetime` a second time and deadlock.
198
+ if (impl_->node && !impl_->data_directory.empty()) {
199
+ const auto contacts = impl_->node->routing_table().good_contacts();
200
+ write_routing_table(routing_table_file_path(), impl_->data_directory,
201
+ impl_->node->self(), contacts);
202
+ LOG_INFO("dht", "saved " << contacts.size() << " contact(s) to disk");
203
+ }
204
+
205
+ impl_->runner.reset();
206
+ impl_->node.reset();
207
+ impl_->transport.reset();
208
+ LOG_INFO("dht", "stopped");
209
+ }
210
+
211
+ void DhtClient::shutdown_immediate() { stop(); }
212
+
213
+ bool DhtClient::is_running() const { return impl_->running.load(); }
214
+
215
+ uint16_t DhtClient::get_port() const {
216
+ const auto lock = impl_->lock_lifetime();
217
+ return impl_->transport ? impl_->transport->port() : 0;
218
+ }
219
+
220
+ bool DhtClient::bootstrap(const std::vector<HostEndpoint>& bootstrap_nodes) {
221
+ const auto lock = impl_->lock_lifetime();
222
+ if (!impl_->running.load()) {
223
+ LOG_WARN("dht", "bootstrap ignored — client not running");
224
+ return false;
225
+ }
226
+ // Resolve hostnames (and filter by family) before the seeds reach the engine: it
227
+ // matches a reply's source address verbatim against the address it queried, so a
228
+ // seed left as a hostname would have every reply dropped as a spoof. Resolution can
229
+ // block, so do it here on the caller's thread, not on the DHT loop.
230
+ auto resolved = resolve_bootstrap_nodes(bootstrap_nodes, impl_->ipv6);
231
+ if (resolved.empty()) {
232
+ LOG_WARN("dht", "bootstrap failed — no usable " << (impl_->ipv6 ? "IPv6" : "IPv4")
233
+ << " node(s) among " << bootstrap_nodes.size() << " seed(s)");
234
+ return false; // nothing usable for our family
235
+ }
236
+ LOG_INFO("dht", "bootstrapping via " << resolved.size() << " seed(s)");
237
+ impl_->runner->post([this, resolved] {
238
+ impl_->node->set_bootstrap_nodes(resolved); // reused by spider reseed
239
+ impl_->node->bootstrap(resolved, Impl::now());
240
+ });
241
+ return true;
242
+ }
243
+
244
+ bool DhtClient::find_peers(const InfoHash& info_hash, PeerDiscoveryCallback callback) {
245
+ const auto lock = impl_->lock_lifetime();
246
+ if (!impl_->running.load()) return false;
247
+ // A user callback runs on the loop thread, so a throw would take the whole DHT
248
+ // down — isolate it here.
249
+ auto safe = [](PeerDiscoveryCallback cb, const std::vector<Address>& p, const InfoHash& h) {
250
+ if (!cb) return;
251
+ try { cb(p, h); } catch (...) {}
252
+ };
253
+ impl_->runner->post([this, info_hash, callback, safe] {
254
+ // Dedup: callers (dht_discovery / bt_client) re-issue find_peers on a refresh
255
+ // timer that fires far faster than a lookup completes (~25s in the wild), so
256
+ // without this they pile up into many concurrent identical traversals hammering
257
+ // the same nodes. If a search for this hash is already running, drop the
258
+ // duplicate — the in-flight lookup keeps delivering peers to its subscriber.
259
+ if (impl_->node->lookup_active(info_hash, /*announce=*/false)) {
260
+ LOG_DEBUG("dht", "find_peers " << dht::short_hex(info_hash)
261
+ << " skipped — search already active");
262
+ return;
263
+ }
264
+ impl_->node->find_peers(
265
+ info_hash,
266
+ [callback, info_hash, safe](const std::vector<Address>& p) { safe(callback, p, info_hash); },
267
+ [callback, info_hash, safe](const std::vector<Address>& all) { safe(callback, all, info_hash); },
268
+ Impl::now());
269
+ });
270
+ return true;
271
+ }
272
+
273
+ bool DhtClient::announce_peer(const InfoHash& info_hash, uint16_t port, PeerDiscoveryCallback callback) {
274
+ const auto lock = impl_->lock_lifetime();
275
+ if (!impl_->running.load()) return false;
276
+ const uint16_t dht_port = impl_->transport->port();
277
+ const uint16_t announce_port = port == 0 ? dht_port : port;
278
+ const bool implied = (announce_port == dht_port);
279
+ // A user callback runs on the loop thread, so a throw would take the whole DHT down —
280
+ // isolate it (same guard as find_peers).
281
+ auto safe = [](PeerDiscoveryCallback cb, const std::vector<Address>& p, const InfoHash& h) {
282
+ if (!cb) return;
283
+ try { cb(p, h); } catch (...) {}
284
+ };
285
+ impl_->runner->post([this, info_hash, announce_port, implied, callback, safe] {
286
+ // Dedup, same rationale as find_peers: don't stack a second announce traversal
287
+ // for a hash that's still announcing (one announce ran ~28s in the wild).
288
+ if (impl_->node->lookup_active(info_hash, /*announce=*/true)) {
289
+ LOG_DEBUG("dht", "announce " << dht::short_hex(info_hash)
290
+ << " skipped — announce already active");
291
+ return;
292
+ }
293
+ impl_->node->announce_peer(
294
+ info_hash, announce_port, implied,
295
+ // Peers stream in as the announce traversal discovers them, then once more in
296
+ // full on completion — so a caller that announces also gets peer discovery for
297
+ // free, with no separate find_peers needed.
298
+ [callback, info_hash, safe](const std::vector<Address>& p) { safe(callback, p, info_hash); },
299
+ [callback, info_hash, safe](const std::vector<Address>& all) { safe(callback, all, info_hash); },
300
+ Impl::now());
301
+ });
302
+ return true;
303
+ }
304
+
305
+ void DhtClient::cancel_search(const InfoHash& info_hash) {
306
+ const auto lock = impl_->lock_lifetime();
307
+ if (!impl_->running.load()) return;
308
+ impl_->runner->post([this, info_hash] { impl_->node->cancel_lookup(info_hash); });
309
+ }
310
+
311
+ NodeId DhtClient::get_node_id() const {
312
+ const auto lock = impl_->lock_lifetime();
313
+ if (impl_->running.load()) return impl_->on_loop([this] { return impl_->node->self(); });
314
+ return impl_->self;
315
+ }
316
+
317
+ void DhtClient::set_external_ip(const std::string& ip_str) {
318
+ const auto lock = impl_->lock_lifetime();
319
+ // STUN and the public API hand us a textual IP — parse once here, then the engine
320
+ // works purely in numeric IpAddress terms (no repeated re-parsing per BEP 42 step).
321
+ const auto ip = IpAddress::parse(ip_str);
322
+ if (!ip) return;
323
+
324
+ if (impl_->running.load()) {
325
+ impl_->runner->post([this, addr = *ip] { impl_->node->set_external_ip(addr); });
326
+ return;
327
+ }
328
+ // Before start: apply BEP 42 to the cached identity so get_node_id() reflects it
329
+ // and the node adopts it at start.
330
+ if (!dht::is_public_address(*ip)) return;
331
+ if (ip->is_v6() != impl_->ipv6) return;
332
+ impl_->external_address = ip_str;
333
+ if (dht::verify_node_id_for_ip(impl_->self, *ip)) return;
334
+ std::mt19937 gen(std::random_device{}());
335
+ NodeId regenerated;
336
+ if (dht::generate_node_id_from_ip(*ip, regenerated, gen)) {
337
+ impl_->self = regenerated;
338
+ LOG_INFO("dht", "external IP " << ip_str << " set before start, node id → "
339
+ << dht::short_hex(impl_->self));
340
+ }
341
+ }
342
+
343
+ std::string DhtClient::get_external_address() const {
344
+ const auto lock = impl_->lock_lifetime();
345
+ if (impl_->running.load())
346
+ return impl_->on_loop([this] { return impl_->node->external_address().to_string(); });
347
+ return impl_->external_address;
348
+ }
349
+
350
+ bool DhtClient::verify_node_id_for_ip(const NodeId& id, const std::string& ip) {
351
+ return dht::verify_node_id_for_ip(id, ip);
352
+ }
353
+
354
+ std::vector<HostEndpoint> DhtClient::get_default_bootstrap_nodes() {
355
+ return dht::Node::default_bootstrap_nodes();
356
+ }
357
+
358
+ size_t DhtClient::get_routing_table_size() const {
359
+ const auto lock = impl_->lock_lifetime();
360
+ if (!impl_->running.load()) return 0;
361
+ return impl_->on_loop([this] { return impl_->node->routing_table().size(); });
362
+ }
363
+
364
+
365
+ bool DhtClient::is_search_active(const InfoHash& info_hash) const {
366
+ const auto lock = impl_->lock_lifetime();
367
+ if (!impl_->running.load()) return false;
368
+ return impl_->on_loop([this, info_hash] { return impl_->node->lookup_active(info_hash, false); });
369
+ }
370
+
371
+ bool DhtClient::is_announce_active(const InfoHash& info_hash) const {
372
+ const auto lock = impl_->lock_lifetime();
373
+ if (!impl_->running.load()) return false;
374
+ return impl_->on_loop([this, info_hash] { return impl_->node->lookup_active(info_hash, true); });
375
+ }
376
+
377
+ size_t DhtClient::get_active_searches_count() const {
378
+ const auto lock = impl_->lock_lifetime();
379
+ if (!impl_->running.load()) return 0;
380
+ return impl_->on_loop([this] { return impl_->node->lookup_count(false); });
381
+ }
382
+
383
+ size_t DhtClient::get_active_announces_count() const {
384
+ const auto lock = impl_->lock_lifetime();
385
+ if (!impl_->running.load()) return 0;
386
+ return impl_->on_loop([this] { return impl_->node->lookup_count(true); });
387
+ }
388
+
389
+ AddressFamily DhtClient::address_family() const { return impl_->family; }
390
+ bool DhtClient::is_ipv6() const { return impl_->ipv6; }
391
+
392
+ std::string DhtClient::routing_table_file_path() const {
393
+ // One routing-table file per data dir (plus the IPv6 variant). The name is
394
+ // deliberately port-independent: an ephemeral (port 0) node binds a different port
395
+ // each run, so a port in the name would mean it never finds its own saved identity
396
+ // + warm set on restart.
397
+ const char* suffix = impl_->ipv6 ? "_v6" : "";
398
+ const std::string name = std::string("dht_routing") + suffix + ".json";
399
+ if (!impl_->data_directory.empty() && impl_->data_directory != ".")
400
+ return impl_->data_directory + "/" + name;
401
+ return name;
402
+ }
403
+
404
+ bool DhtClient::save_routing_table() {
405
+ const auto lock = impl_->lock_lifetime();
406
+ NodeId self = impl_->self;
407
+ std::vector<dht::NodeEntry> contacts;
408
+ if (impl_->running.load() && impl_->node) {
409
+ auto snap = impl_->on_loop([this] {
410
+ return std::make_pair(impl_->node->self(), impl_->node->routing_table().good_contacts());
411
+ });
412
+ self = snap.first;
413
+ contacts = std::move(snap.second);
414
+ } else if (impl_->node) {
415
+ self = impl_->node->self();
416
+ contacts = impl_->node->routing_table().good_contacts();
417
+ }
418
+ return write_routing_table(routing_table_file_path(), impl_->data_directory, self, contacts);
419
+ }
420
+
421
+ bool DhtClient::load_routing_table() {
422
+ const auto lock = impl_->lock_lifetime();
423
+ NodeId loaded = impl_->self;
424
+ std::vector<dht::NodeEntry> contacts;
425
+ if (!dht::load_routing_table(routing_table_file_path(), loaded, contacts)) return false;
426
+ impl_->self = loaded;
427
+ if (impl_->running.load() && impl_->node)
428
+ impl_->on_loop([this, &contacts] { impl_->node->routing_table().load_contacts(contacts); return 0; });
429
+ LOG_DEBUG("dht", "loaded " << contacts.size() << " contact(s) from disk");
430
+ return true;
431
+ }
432
+
433
+ void DhtClient::set_data_directory(const std::string& directory) { impl_->data_directory = directory; }
434
+
435
+ #ifdef RATS_SEARCH_FEATURES
436
+ void DhtClient::set_spider_mode(bool enable) {
437
+ const auto lock = impl_->lock_lifetime();
438
+ if (impl_->running.load()) impl_->node->set_spider_mode(enable); // atomic flag, loop-safe
439
+ }
440
+ bool DhtClient::is_spider_mode() const {
441
+ const auto lock = impl_->lock_lifetime();
442
+ return impl_->running.load() && impl_->node->is_spider_mode(); // atomic read, loop-safe
443
+ }
444
+ void DhtClient::set_spider_announce_callback(SpiderAnnounceCallback callback) {
445
+ const auto lock = impl_->lock_lifetime();
446
+ if (impl_->running.load())
447
+ impl_->runner->post([this, callback]() mutable { impl_->node->set_spider_announce_callback(std::move(callback)); });
448
+ }
449
+ void DhtClient::set_spider_ignore(bool ignore) {
450
+ const auto lock = impl_->lock_lifetime();
451
+ if (impl_->running.load()) impl_->node->set_spider_ignore(ignore); // atomic
452
+ }
453
+ bool DhtClient::is_spider_ignoring() const {
454
+ const auto lock = impl_->lock_lifetime();
455
+ return impl_->running.load() && impl_->node->is_spider_ignoring();
456
+ }
457
+ void DhtClient::spider_walk() {
458
+ const auto lock = impl_->lock_lifetime();
459
+ if (impl_->running.load()) impl_->runner->post([this] { impl_->node->spider_walk(Impl::now()); });
460
+ }
461
+ size_t DhtClient::get_spider_pool_size() const {
462
+ const auto lock = impl_->lock_lifetime();
463
+ if (!impl_->running.load()) return 0;
464
+ return impl_->on_loop([this] { return impl_->node->spider_pool_size(); });
465
+ }
466
+ size_t DhtClient::get_spider_visited_count() const {
467
+ const auto lock = impl_->lock_lifetime();
468
+ if (!impl_->running.load()) return 0;
469
+ return impl_->on_loop([this] { return impl_->node->spider_visited_count(); });
470
+ }
471
+ void DhtClient::clear_spider_state() {
472
+ const auto lock = impl_->lock_lifetime();
473
+ if (impl_->running.load()) impl_->runner->post([this] { impl_->node->clear_spider_state(); });
474
+ }
475
+ #endif // RATS_SEARCH_FEATURES
476
+
477
+ std::vector<Address> resolve_bootstrap_nodes(const std::vector<HostEndpoint>& nodes, bool ipv6) {
478
+ std::vector<Address> out;
479
+ out.reserve(nodes.size());
480
+ for (const auto& n : nodes) {
481
+ if (n.host.empty() || n.port == 0) continue;
482
+
483
+ // Numeric literal: keep it only if it belongs to this node's family.
484
+ if (auto ip = IpAddress::parse(n.host)) {
485
+ if (ip->is_v6() == ipv6) out.push_back(Address{*ip, n.port});
486
+ continue;
487
+ }
488
+
489
+ // Hostname: resolve to our family (A for IPv4, AAAA for IPv6). Drop it if it
490
+ // has no record there, rather than feed the engine an unmatchable hostname.
491
+ const std::string ip = ipv6 ? network_utils::resolve_hostname_v6(n.host)
492
+ : network_utils::resolve_hostname(n.host);
493
+ if (auto a = IpAddress::parse(ip)) out.push_back(Address{*a, n.port});
494
+ }
495
+ return out;
496
+ }
497
+
498
+ // ---- node id -> hex (for logging) ------------------------------------------
499
+ std::string node_id_to_hex(const NodeId& id) { return dht::to_hex(id); }
500
+
501
+ } // namespace librats
@@ -0,0 +1,119 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file dht.h
5
+ * @brief Public DHT client — a thin facade over the modular dht::Node engine.
6
+ *
7
+ * This is the long-standing `librats::DhtClient` API every consumer already uses. It
8
+ * now wraps the rewritten engine (src/dht/node.h + friends): a single-threaded,
9
+ * lock-free dht::Node driven by a DhtRunner over a UdpTransport. The facade owns that
10
+ * trio and marshals public calls onto the runner's thread, so callers keep the same
11
+ * simple, thread-safe surface they always had.
12
+ *
13
+ * The engine internals live in namespace librats::dht; this header only exposes the
14
+ * stable public types so consumers (and their includes) are unchanged.
15
+ */
16
+
17
+ #include "librats/util/rats_export.h"
18
+ #include "librats/core/address.h"
19
+ #include "librats/core/host_endpoint.h"
20
+ #include "librats/core/socket.h" // AddressFamily
21
+ #include "librats/dht/krpc.h" // KrpcProtocol/KrpcMessage (wire format, used by tests)
22
+
23
+ #include <array>
24
+ #include <chrono>
25
+ #include <cstdint>
26
+ #include <functional>
27
+ #include <memory>
28
+ #include <random>
29
+ #include <string>
30
+ #include <vector>
31
+
32
+ namespace librats {
33
+
34
+ // Standard BitTorrent DHT port. The public NodeId/InfoHash id types come from
35
+ // dht/krpc.h (included above), so the facade re-exposes them without duplicating
36
+ // the engine-internal dht::NodeId.
37
+ constexpr int DHT_PORT = 6881;
38
+
39
+ /// Discovered peers for an info-hash are delivered through this callback.
40
+ using PeerDiscoveryCallback = std::function<void(const std::vector<Address>& peers, const InfoHash& info_hash)>;
41
+
42
+ #ifdef RATS_SEARCH_FEATURES
43
+ /// Spider mode: invoked when a peer announces it has a torrent.
44
+ using SpiderAnnounceCallback = std::function<void(const InfoHash& info_hash, const Address& peer)>;
45
+ #endif
46
+
47
+ /**
48
+ * Kademlia DHT client (BEP 5/32/42). One instance serves one address family.
49
+ */
50
+ class RATS_API DhtClient {
51
+ public:
52
+ DhtClient(int port = DHT_PORT, const std::string& bind_address = "",
53
+ const std::string& data_directory = "",
54
+ AddressFamily address_family = AddressFamily::IPv4);
55
+ ~DhtClient();
56
+
57
+ DhtClient(const DhtClient&) = delete;
58
+ DhtClient& operator=(const DhtClient&) = delete;
59
+
60
+ bool start();
61
+ void stop();
62
+ void shutdown_immediate();
63
+ bool is_running() const;
64
+ uint16_t get_port() const;
65
+
66
+ bool bootstrap(const std::vector<HostEndpoint>& bootstrap_nodes);
67
+ bool find_peers(const InfoHash& info_hash, PeerDiscoveryCallback callback);
68
+ bool announce_peer(const InfoHash& info_hash, uint16_t port = 0, PeerDiscoveryCallback callback = nullptr);
69
+ void cancel_search(const InfoHash& info_hash);
70
+
71
+ NodeId get_node_id() const;
72
+ void set_external_ip(const std::string& ip);
73
+ std::string get_external_address() const;
74
+ static bool verify_node_id_for_ip(const NodeId& id, const std::string& ip);
75
+ static std::vector<HostEndpoint> get_default_bootstrap_nodes();
76
+
77
+ size_t get_routing_table_size() const;
78
+ bool is_search_active(const InfoHash& info_hash) const;
79
+ bool is_announce_active(const InfoHash& info_hash) const;
80
+ size_t get_active_searches_count() const;
81
+ size_t get_active_announces_count() const;
82
+
83
+ AddressFamily address_family() const;
84
+ bool is_ipv6() const;
85
+
86
+ std::string routing_table_file_path() const;
87
+ bool save_routing_table();
88
+ bool load_routing_table();
89
+ void set_data_directory(const std::string& directory);
90
+
91
+ #ifdef RATS_SEARCH_FEATURES
92
+ void set_spider_mode(bool enable);
93
+ bool is_spider_mode() const;
94
+ void set_spider_announce_callback(SpiderAnnounceCallback callback);
95
+ void set_spider_ignore(bool ignore);
96
+ bool is_spider_ignoring() const;
97
+ void spider_walk();
98
+ size_t get_spider_pool_size() const;
99
+ size_t get_spider_visited_count() const;
100
+ void clear_spider_state();
101
+ #endif
102
+
103
+ private:
104
+ struct Impl;
105
+ std::unique_ptr<Impl> impl_;
106
+ };
107
+
108
+ // Resolve any hostname bootstrap entries to numeric IPs of the given family, dropping
109
+ // wrong-family and unresolvable ones. The DHT engine matches a reply's source address
110
+ // verbatim against the address it queried (anti-spoofing), so a seed kept as a hostname
111
+ // would have every reply dropped — seeds must enter the engine as numeric IPs of the
112
+ // right family. Resolution can block, so callers run this off the DHT loop thread.
113
+ // Exposed for testing.
114
+ std::vector<Address> resolve_bootstrap_nodes(const std::vector<HostEndpoint>& nodes, bool ipv6);
115
+
116
+ // Node id -> lowercase hex, for logging.
117
+ std::string node_id_to_hex(const NodeId& id);
118
+
119
+ } // namespace librats