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,473 @@
1
+ #include "librats/dht/node.h"
2
+ #include "librats/dht/announce.h"
3
+ #include "librats/dht/bep42.h"
4
+ #include "librats/dht/krpc.h"
5
+ #include "librats/dht/log.h"
6
+
7
+ #include <algorithm>
8
+ #include <chrono>
9
+ #include <random>
10
+ #include <sstream>
11
+ #include <utility>
12
+
13
+ namespace librats {
14
+ namespace dht {
15
+
16
+ namespace {
17
+
18
+ // Convert routing-table contacts to the wire form used in find_node/get_peers replies.
19
+ std::vector<KrpcNode> to_krpc(const std::vector<NodeEntry>& nodes) {
20
+ std::vector<KrpcNode> out;
21
+ out.reserve(nodes.size());
22
+ for (const auto& n : nodes) out.emplace_back(n.id, n.endpoint.ip, n.endpoint.port);
23
+ return out;
24
+ }
25
+
26
+ // A capped, comma-joined rendering of peer endpoints for a log line: show the first few
27
+ // and summarise the rest, so even a large result set stays one readable line. Only ever
28
+ // called inside a LOG_* argument, so it costs nothing when the level is filtered out.
29
+ std::string format_peers(const std::vector<Address>& peers, std::size_t max = 6) {
30
+ std::ostringstream s;
31
+ const std::size_t shown = (std::min)(peers.size(), max);
32
+ for (std::size_t i = 0; i < shown; ++i) {
33
+ if (i) s << ", ";
34
+ s << peers[i].to_string();
35
+ }
36
+ if (peers.size() > shown) s << " (+" << (peers.size() - shown) << " more)";
37
+ return s.str();
38
+ }
39
+
40
+ // A bare liveness ping. Owned solely by the RpcManager while in flight; on the
41
+ // outcome it just records the contact's liveness in the routing table. This is the
42
+ // "ping" half of ping-before-replace — a timeout here lets the table promote a standby.
43
+ class PingObserver : public Observer {
44
+ public:
45
+ PingObserver(RoutingTable& table, const NodeId& id, const Address& ep)
46
+ : Observer(id, ep), table_(table) {}
47
+
48
+ void on_response(const KrpcMessage& msg, uint16_t rtt, TimePoint) override {
49
+ // BEP 42: a contact whose id is derived from its IP is harder to spoof and is
50
+ // preferred when the table has to choose who to drop.
51
+ const bool verified = verify_node_id_for_ip(msg.response_id, endpoint_.ip);
52
+ table_.node_seen(msg.response_id, endpoint_, rtt, verified);
53
+ }
54
+ void on_timeout(TimePoint) override { table_.node_failed(id_, endpoint_); }
55
+ void on_short_timeout(TimePoint) override {}
56
+
57
+ private:
58
+ RoutingTable& table_;
59
+ };
60
+
61
+ } // namespace
62
+
63
+ Node::Node(Transport& transport, const NodeId& self, bool ipv6)
64
+ : transport_(transport), self_(self), ipv6_(ipv6), table_(self), rpc_(transport) {}
65
+
66
+ void Node::on_datagram(const std::vector<uint8_t>& data, const Address& from, TimePoint now) {
67
+ now_ = now;
68
+ if (!dos_.allow(from.ip, now)) return; // flooding source — ignore
69
+
70
+ auto msg = KrpcProtocol::decode_message(data);
71
+ if (!msg) return;
72
+
73
+ switch (msg->type) {
74
+ case KrpcMessageType::Query: handle_query(*msg, from, now); break;
75
+ case KrpcMessageType::Response: handle_response(*msg, from, now); break;
76
+ case KrpcMessageType::Error: rpc_.handle_response(*msg, from, now); break; // → observer timeout
77
+ }
78
+ }
79
+
80
+ void Node::handle_query(const KrpcMessage& msg, const Address& from, TimePoint now) {
81
+ #ifdef RATS_SEARCH_FEATURES
82
+ const bool spider = spider_mode_.load();
83
+ // While ignoring, still take announce_peer (the whole point of a crawl) but drop the rest.
84
+ if (spider && spider_ignore_.load() && msg.query_type != KrpcQueryType::AnnouncePeer) return;
85
+ // Answer with a neighbour id only to IPs we've crawled, so we don't pollute organic peers.
86
+ const bool use_neighbor = spider && spider_contacted(from.ip);
87
+ if (spider) add_spider_node(NodeEntry(msg.sender_id, from));
88
+ #else
89
+ const bool use_neighbor = false;
90
+ #endif
91
+
92
+ LOG_DEBUG("dht.rpc", "<- " << KrpcProtocol::query_type_to_string(msg.query_type)
93
+ << " from " << from.to_string() << " (" << short_hex(msg.sender_id) << ")");
94
+
95
+ // Organic senders join the routing table; crawled IPs are kept out of it.
96
+ auto learn = [&](const NodeId& sender) {
97
+ // A node that merely *queries* us hasn't proven a round-trip: the UDP source
98
+ // address can be spoofed, so we must not trust it as a confirmed contact (which
99
+ // we'd hand out in find_node replies and let split buckets). Record it as
100
+ // heard_about — an unconfirmed candidate that a refresh ping must verify before
101
+ // we route with it. We still carry the BEP 42 verdict on the (id, ip) pair as a
102
+ // tie-breaker among candidates.
103
+ if (!use_neighbor)
104
+ table_.heard_about(sender, from, verify_node_id_for_ip(sender, from.ip));
105
+ };
106
+ auto reply_id = [&](const NodeId& anchor) -> NodeId {
107
+ #ifdef RATS_SEARCH_FEATURES
108
+ if (use_neighbor) return neighbor_id(anchor);
109
+ #endif
110
+ (void)anchor;
111
+ return self_;
112
+ };
113
+
114
+ switch (msg.query_type) {
115
+ case KrpcQueryType::Ping: {
116
+ learn(msg.sender_id);
117
+ send_message(KrpcProtocol::create_ping_response(msg.transaction_id, reply_id(msg.sender_id)), from);
118
+ break;
119
+ }
120
+ case KrpcQueryType::FindNode: {
121
+ learn(msg.sender_id);
122
+ auto nodes = to_krpc(table_.find_closest(msg.target_id, kBucketSize));
123
+ send_message(KrpcProtocol::create_find_node_response(msg.transaction_id, reply_id(msg.target_id), nodes), from);
124
+ break;
125
+ }
126
+ case KrpcQueryType::GetPeers: {
127
+ learn(msg.sender_id);
128
+ const std::string token = tokens_.generate(from, msg.info_hash, now);
129
+ const auto peers = peers_.get(msg.info_hash);
130
+ const NodeId rid = reply_id(msg.info_hash);
131
+ KrpcMessage reply = peers.empty()
132
+ ? KrpcProtocol::create_get_peers_response_with_nodes(
133
+ msg.transaction_id, rid, to_krpc(table_.find_closest(msg.info_hash, kBucketSize)), token)
134
+ : KrpcProtocol::create_get_peers_response(msg.transaction_id, rid, peers, token);
135
+ send_message(reply, from);
136
+ break;
137
+ }
138
+ case KrpcQueryType::AnnouncePeer: {
139
+ #ifdef RATS_SEARCH_FEATURES
140
+ const bool skip_token = spider; // crawl collects every announce it can
141
+ #else
142
+ const bool skip_token = false;
143
+ #endif
144
+ if (!skip_token && !tokens_.verify(from, msg.info_hash, msg.token, now)) {
145
+ LOG_DEBUG("dht.rpc", "announce_peer from " << from.to_string() << " rejected: invalid token");
146
+ send_message(KrpcProtocol::create_error(msg.transaction_id,
147
+ KrpcErrorCode::ProtocolError, "Invalid token"), from);
148
+ return;
149
+ }
150
+ learn(msg.sender_id);
151
+ const uint16_t port = msg.implied_port ? from.port : msg.port;
152
+ const Address peer(from.ip, port);
153
+ peers_.store(msg.info_hash, peer, now);
154
+ #ifdef RATS_SEARCH_FEATURES
155
+ if (spider && spider_announce_) spider_announce_(msg.info_hash, peer);
156
+ #endif
157
+ send_message(KrpcProtocol::create_announce_peer_response(msg.transaction_id, reply_id(msg.info_hash)), from);
158
+ break;
159
+ }
160
+ }
161
+ }
162
+
163
+ void Node::handle_response(const KrpcMessage& msg, const Address& from, TimePoint now) {
164
+ if (!msg.external_ip.is_unspecified()) maybe_update_external_ip(msg.external_ip, from);
165
+ // The matching observer (a lookup or a ping) feeds the routing table and drives
166
+ // its lookup; anti-spoofing and timeouts live in the RpcManager.
167
+ rpc_.handle_response(msg, from, now);
168
+ }
169
+
170
+ void Node::send_message(const KrpcMessage& msg, const Address& to) {
171
+ KrpcMessage out = msg;
172
+ // BEP 42: tell the requester the address we see them at, so they can derive a
173
+ // compliant node id (and so we learn our own address from their replies).
174
+ if (out.type == KrpcMessageType::Response && out.external_ip.is_unspecified()) {
175
+ out.external_ip = to.ip; // already numeric bytes — no string round-trip
176
+ out.external_port = to.port;
177
+ }
178
+ const auto data = KrpcProtocol::encode_message(out);
179
+ if (!data.empty()) transport_.send(to, data);
180
+ }
181
+
182
+ void Node::ping(const NodeId& id, const Address& ep, TimePoint now) {
183
+ auto obs = std::make_shared<PingObserver>(table_, id, ep);
184
+ KrpcMessage q = KrpcProtocol::create_ping_query("", self_);
185
+ rpc_.invoke(q, ep, obs, now); // the RpcManager owns obs until it resolves
186
+ }
187
+
188
+ FindPeers& Node::start_lookup(std::unique_ptr<FindPeers> lookup, TimePoint now) {
189
+ now_ = now; // so a synchronous completion inside start() measures a sane duration
190
+ lookup->set_want(want());
191
+ // Self-heal: when the routing table is too thin to seed a lookup (cold start, or every
192
+ // contact has died), fall back to the bootstrap routers — mirrors libtorrent's
193
+ // traversal_algorithm::add_router_entries(). Without this, a node whose table empties
194
+ // can never recover: every lookup would converge instantly on an empty candidate set,
195
+ // and the self-refresh in tick() never fires while the table is empty.
196
+ if (table_.size() < kAlpha)
197
+ for (const auto& ep : bootstrap_nodes_) lookup->add_seed(ep);
198
+ FindPeers& ref = *lookup;
199
+ lookups_.push_back(std::move(lookup));
200
+ ref.start(now); // may complete synchronously (e.g. empty table) — reaped next tick
201
+ return ref;
202
+ }
203
+
204
+ void Node::bootstrap(const std::vector<Address>& nodes, TimePoint now) {
205
+ now_ = now; // bootstrap seeds and starts its lookup without going through start_lookup
206
+ // A self-targeted get_peers lookup populates the table with our neighbourhood; on
207
+ // completion we report how warm the table got.
208
+ auto boot = std::make_unique<FindPeers>(table_, rpc_, self_, self_,
209
+ FindPeers::PeersCallback{},
210
+ [this](const std::vector<Address>&) {
211
+ LOG_INFO("dht.find", "bootstrap complete — routing table: " << table_.size()
212
+ << " node(s), " << table_.bucket_count() << " bucket(s)");
213
+ });
214
+ LOG_DEBUG("dht.find", "bootstrap lookup L" << boot->id() << ": " << nodes.size()
215
+ << " seed(s), table=" << table_.size());
216
+ boot->set_want(want());
217
+ for (const auto& ep : nodes) boot->add_seed(ep);
218
+ FindPeers& ref = *boot;
219
+ lookups_.push_back(std::move(boot));
220
+ ref.start(now);
221
+ }
222
+
223
+ void Node::find_peers(const InfoHash& info_hash, FindPeers::PeersCallback on_peers,
224
+ FindPeers::DoneCallback on_done, TimePoint now) {
225
+ // Peers as they arrive — the actual endpoints, so progress is visible mid-lookup.
226
+ auto peers = [this, info_hash, cb = std::move(on_peers)](const std::vector<Address>& fresh) {
227
+ LOG_DEBUG("dht.find", "find_peers " << short_hex(info_hash) << ": +" << fresh.size()
228
+ << " peer(s): " << format_peers(fresh));
229
+ if (cb) cb(fresh);
230
+ };
231
+ // The result line: total peers, how long it took, and (capped) which ones.
232
+ auto done = [this, info_hash, start = now, cb = std::move(on_done)](const std::vector<Address>& all) {
233
+ const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now_ - start).count();
234
+ LOG_INFO("dht.find", "find_peers " << short_hex(info_hash) << " → " << all.size()
235
+ << " peer(s) in " << (ms < 0 ? 0 : ms) << "ms"
236
+ << (all.empty() ? "" : ": " + format_peers(all)));
237
+ if (cb) cb(all);
238
+ };
239
+ auto lookup = std::make_unique<FindPeers>(table_, rpc_, self_, info_hash,
240
+ std::move(peers), std::move(done));
241
+ LOG_DEBUG("dht.find", "find_peers " << short_hex(info_hash) << " L" << lookup->id()
242
+ << " started (table=" << table_.size() << ")");
243
+ start_lookup(std::move(lookup), now);
244
+ }
245
+
246
+ void Node::announce_peer(const InfoHash& info_hash, uint16_t port, bool implied_port,
247
+ FindPeers::PeersCallback on_peers, FindPeers::DoneCallback on_done, TimePoint now) {
248
+ // An announce is a get_peers traversal that announces on completion, so it discovers
249
+ // peers along the way too. Stream them as they arrive (like find_peers), so a caller
250
+ // that both announces and wants peers needs only this one lookup, not a second one.
251
+ auto peers = [this, info_hash, cb = std::move(on_peers)](const std::vector<Address>& fresh) {
252
+ LOG_DEBUG("dht.find", "announce " << short_hex(info_hash) << ": +" << fresh.size()
253
+ << " peer(s): " << format_peers(fresh));
254
+ if (cb) cb(fresh);
255
+ };
256
+ auto done = [this, info_hash, start = now, cb = std::move(on_done)](const std::vector<Address>& all) {
257
+ const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now_ - start).count();
258
+ LOG_INFO("dht.find", "announce " << short_hex(info_hash) << " complete in "
259
+ << (ms < 0 ? 0 : ms) << "ms (" << all.size() << " peer(s) seen)");
260
+ if (cb) cb(all);
261
+ };
262
+ auto lookup = std::make_unique<Announce>(table_, rpc_, self_, info_hash, port, implied_port,
263
+ std::move(peers), std::move(done));
264
+ LOG_DEBUG("dht.find", "announce " << short_hex(info_hash) << " L" << lookup->id() << " on port " << port
265
+ << (implied_port ? " (implied)" : "") << " started (table=" << table_.size() << ")");
266
+ start_lookup(std::move(lookup), now);
267
+ }
268
+
269
+ void Node::cancel_lookup(const InfoHash& info_hash) {
270
+ // Erasing a Traversal cancels its in-flight RPCs in its destructor.
271
+ lookups_.erase(std::remove_if(lookups_.begin(), lookups_.end(),
272
+ [&](const std::unique_ptr<Traversal>& t) { return t->target() == info_hash; }),
273
+ lookups_.end());
274
+ }
275
+
276
+ bool Node::lookup_active(const InfoHash& info_hash, bool announce) const {
277
+ for (const auto& t : lookups_)
278
+ if (!t->finished() && t->is_announce() == announce && t->target() == info_hash) return true;
279
+ return false;
280
+ }
281
+
282
+ std::size_t Node::lookup_count(bool announce) const {
283
+ std::size_t n = 0;
284
+ for (const auto& t : lookups_)
285
+ if (!t->finished() && t->is_announce() == announce) ++n;
286
+ return n;
287
+ }
288
+
289
+ void Node::tick(TimePoint now) {
290
+ now_ = now;
291
+ rpc_.tick(now); // fire query timeouts (drives lookups + ping-before-replace)
292
+ reap_finished();
293
+ peers_.expire(now); // drop announced peers older than the TTL
294
+
295
+ // Refresh one stale contact per interval (confirms hearsay, prunes dead nodes).
296
+ if (now - last_refresh_ >= kRefreshInterval) {
297
+ if (auto stale = table_.next_to_refresh(now)) ping(stale->id, stale->endpoint, now);
298
+ last_refresh_ = now;
299
+ }
300
+
301
+ // Periodically re-explore our own neighbourhood to keep the table fresh.
302
+ if (now - last_self_refresh_ >= kSelfRefreshInterval && lookups_.empty() && !table_.empty()) {
303
+ start_lookup(std::make_unique<FindPeers>(table_, rpc_, self_, self_,
304
+ FindPeers::PeersCallback{}, FindPeers::DoneCallback{}), now);
305
+ last_self_refresh_ = now;
306
+ }
307
+
308
+ // DEBUG heartbeat: periodic activity counters plus a pretty routing-table dump, so
309
+ // the live state is visible over time without tracing every event. describe() is only
310
+ // built when DEBUG is actually enabled (the macro guards on the level).
311
+ if (now - last_state_log_ >= kStateLogInterval) {
312
+ LOG_DEBUG("dht", "activity: " << lookups_.size() << " lookup(s), "
313
+ << rpc_.outstanding() << " rpc in-flight, "
314
+ << peers_.hash_count() << " stored hash(es)");
315
+ LOG_DEBUG("dht.route", table_.describe());
316
+ last_state_log_ = now;
317
+ }
318
+ }
319
+
320
+ void Node::reap_finished() {
321
+ lookups_.erase(std::remove_if(lookups_.begin(), lookups_.end(),
322
+ [](const std::unique_ptr<Traversal>& t) { return t->finished(); }),
323
+ lookups_.end());
324
+ }
325
+
326
+ void Node::maybe_update_external_ip(const IpAddress& reported_ip, const Address& from) {
327
+ if (!is_public_address(reported_ip)) return; // IpAddress overload — no re-parse
328
+ if (reported_ip.is_v6() != ipv6_) return; // wrong family
329
+ if (reported_ip == external_address_) return;
330
+
331
+ if (!ip_voters_.insert(from.ip).second) return; // one vote per distinct responder
332
+ const int votes = ++ip_votes_[reported_ip];
333
+ LOG_DEBUG("dht", "external IP vote: " << reported_ip.to_string() << " (" << votes << "/"
334
+ << kExternalIpVoteThreshold << ", from " << from.ip.to_string() << ")");
335
+ if (votes >= kExternalIpVoteThreshold) {
336
+ ip_votes_.clear();
337
+ ip_voters_.clear();
338
+ set_external_ip(reported_ip);
339
+ }
340
+ }
341
+
342
+ void Node::set_external_ip(const IpAddress& ip) {
343
+ if (!is_public_address(ip)) return;
344
+ if (ip.is_v6() != ipv6_) return; // separate networks per family
345
+ const bool changed = (ip != external_address_);
346
+ external_address_ = ip;
347
+
348
+ if (verify_node_id_for_ip(self_, ip)) { // our id already matches this address
349
+ if (changed) LOG_INFO("dht", "external address is " << ip.to_string() << " (node id already BEP 42-compliant)");
350
+ return;
351
+ }
352
+
353
+ std::mt19937 gen(std::random_device{}());
354
+ NodeId new_id;
355
+ if (!generate_node_id_from_ip(ip, new_id, gen)) return;
356
+ self_ = new_id;
357
+ table_.set_self(new_id); // re-bucket everything against the new id
358
+ LOG_INFO("dht", "external address is " << ip.to_string() << ", regenerated node id "
359
+ << short_hex(self_) << " (BEP 42)");
360
+ }
361
+
362
+ std::vector<HostEndpoint> Node::default_bootstrap_nodes() {
363
+ // Hostnames resolve per-family at send time; dht.libtorrent.org also has an AAAA
364
+ // record, giving IPv6 a reliable entry point.
365
+ return {
366
+ {"router.bittorrent.com", 6881},
367
+ {"dht.transmissionbt.com", 6881},
368
+ {"router.utorrent.com", 6881},
369
+ {"dht.libtorrent.org", 25401},
370
+ {"dht.aelitis.com", 6881},
371
+ };
372
+ }
373
+
374
+ #ifdef RATS_SEARCH_FEATURES
375
+
376
+ namespace {
377
+ // Forwards a successful reply to a callback and ignores timeouts. Owned solely by the
378
+ // RpcManager while in flight — used by the spider crawl to feed discovered nodes back.
379
+ class CallbackObserver : public Observer {
380
+ public:
381
+ using Fn = std::function<void(const KrpcMessage&, const Address&)>;
382
+ CallbackObserver(const NodeId& id, const Address& ep, Fn on_reply)
383
+ : Observer(id, ep), on_reply_(std::move(on_reply)) {}
384
+ void on_response(const KrpcMessage& msg, uint16_t, TimePoint) override {
385
+ if (on_reply_) on_reply_(msg, endpoint_);
386
+ }
387
+ void on_timeout(TimePoint) override {}
388
+ void on_short_timeout(TimePoint) override {}
389
+
390
+ private:
391
+ Fn on_reply_;
392
+ };
393
+ } // namespace
394
+
395
+ NodeId Node::neighbor_id(const NodeId& target) const {
396
+ NodeId out;
397
+ std::copy(target.begin(), target.begin() + 10, out.begin()); // look close to the target
398
+ std::copy(self_.begin() + 10, self_.end(), out.begin() + 10); // but still uniquely us
399
+ return out;
400
+ }
401
+
402
+ void Node::add_spider_node(const NodeEntry& node) {
403
+ for (auto& n : spider_pool_)
404
+ if (n.id == node.id) { n.endpoint = node.endpoint; return; }
405
+ if (spider_pool_.size() < kMaxSpiderNodes) {
406
+ spider_pool_.push_back(node);
407
+ return;
408
+ }
409
+ static thread_local std::mt19937 gen(std::random_device{}()); // pool full → evict a random slot
410
+ spider_pool_[std::uniform_int_distribution<std::size_t>(0, spider_pool_.size() - 1)(gen)] = node;
411
+ }
412
+
413
+ bool Node::spider_contacted(const IpAddress& ip) const {
414
+ return spider_contacted_ips_.count(ip) > 0;
415
+ }
416
+
417
+ void Node::spider_absorb(const KrpcMessage& msg, const Address& from) {
418
+ add_spider_node(NodeEntry(msg.response_id, from));
419
+ for (const auto& n : msg.nodes) add_spider_node(NodeEntry(n.id, Address(n.ip, n.port)));
420
+ }
421
+
422
+ void Node::clear_spider_state() {
423
+ spider_pool_.clear();
424
+ spider_visited_.clear();
425
+ spider_contacted_ips_.clear();
426
+ }
427
+
428
+ void Node::spider_walk(TimePoint now) {
429
+ static thread_local std::mt19937 gen(std::random_device{}());
430
+
431
+ if (spider_visited_.size() >= kMaxSpiderVisited) spider_visited_.clear();
432
+ if (spider_contacted_ips_.size() >= kMaxSpiderContactedIps) spider_contacted_ips_.clear();
433
+
434
+ if (spider_pool_.empty()) {
435
+ for (const auto& n : table_.find_closest(self_, kMaxSpiderNodes, true)) {
436
+ if (spider_pool_.size() >= kMaxSpiderNodes) break;
437
+ spider_pool_.push_back(n);
438
+ }
439
+ if (spider_pool_.empty()) {
440
+ if (now - last_spider_bootstrap_ >= std::chrono::seconds(30)) {
441
+ last_spider_bootstrap_ = now;
442
+ // Reseed from the resolved seed set the facade gave us. The engine has no
443
+ // resolver, so it can only reuse already-resolved Addresses — the hostname
444
+ // defaults live at the facade layer (DhtClient) and are resolved there.
445
+ if (!bootstrap_nodes_.empty()) bootstrap(bootstrap_nodes_, now);
446
+ }
447
+ return;
448
+ }
449
+ }
450
+
451
+ auto pick = [&] { return std::uniform_int_distribution<std::size_t>(0, spider_pool_.size() - 1)(gen); };
452
+ NodeEntry target = spider_pool_[pick()];
453
+ for (int i = 0; i < 10; ++i) {
454
+ const NodeEntry& cand = spider_pool_[pick()];
455
+ if (spider_visited_.find(cand.id) == spider_visited_.end()) { target = cand; break; }
456
+ }
457
+ spider_visited_.insert(target.id);
458
+ spider_contacted_ips_.insert(target.endpoint.ip);
459
+
460
+ NodeId random_target;
461
+ { std::uniform_int_distribution<int> b(0, 255); for (auto& x : random_target) x = static_cast<uint8_t>(b(gen)); }
462
+
463
+ // find_node toward a random target, posing as a neighbour of the node we ask.
464
+ KrpcMessage q = KrpcProtocol::create_find_node_query("", neighbor_id(target.id), random_target);
465
+ auto obs = std::make_shared<CallbackObserver>(target.id, target.endpoint,
466
+ [this](const KrpcMessage& m, const Address& ep) { spider_absorb(m, ep); });
467
+ rpc_.invoke(q, target.endpoint, obs, now);
468
+ }
469
+
470
+ #endif // RATS_SEARCH_FEATURES
471
+
472
+ } // namespace dht
473
+ } // namespace librats
@@ -0,0 +1,164 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file node.h
5
+ * @brief The Kademlia engine: routing table + RPC + storage + lookups, wired together.
6
+ *
7
+ * Pure logic, no sockets and no threads — it sends through an injected Transport and
8
+ * is driven from outside by on_datagram() (a packet arrived) and tick() (time passed).
9
+ * That makes the whole DHT unit-testable with a mock transport. A thin UdpTransport +
10
+ * DhtClient facade (Phase 5) supply the real socket and threading.
11
+ *
12
+ * One node serves a single address family (IPv4 or IPv6 — separate Kademlia networks
13
+ * per BEP 32). It answers incoming queries (ping/find_node/get_peers/announce_peer)
14
+ * and runs its own lookups (bootstrap, find_peers, announce_peer).
15
+ */
16
+
17
+ #include "librats/core/address.h"
18
+ #include "librats/core/host_endpoint.h"
19
+ #include "librats/dht/dos_blocker.h"
20
+ #include "librats/dht/find_peers.h"
21
+ #include "librats/dht/id.h"
22
+ #include "librats/dht/observer.h"
23
+ #include "librats/dht/routing_table.h"
24
+ #include "librats/dht/rpc_manager.h"
25
+ #include "librats/dht/storage.h"
26
+ #include "librats/dht/transport.h"
27
+
28
+ #include <atomic>
29
+ #include <chrono>
30
+ #include <cstddef>
31
+ #include <cstdint>
32
+ #include <functional>
33
+ #include <map>
34
+ #include <memory>
35
+ #include <random>
36
+ #include <set>
37
+ #include <string>
38
+ #include <unordered_set>
39
+ #include <vector>
40
+
41
+ namespace librats {
42
+ struct KrpcMessage;
43
+
44
+ namespace dht {
45
+
46
+ #ifdef RATS_SEARCH_FEATURES
47
+ // Invoked when another peer announces it has a torrent (spider mode collects these).
48
+ using SpiderAnnounceCallback = std::function<void(const InfoHash&, const Address&)>;
49
+ #endif
50
+
51
+ class Node {
52
+ public:
53
+ Node(Transport& transport, const NodeId& self, bool ipv6);
54
+
55
+ // -- driven by the transport layer -----------------------------------------
56
+ void on_datagram(const std::vector<uint8_t>& data, const Address& from, TimePoint now);
57
+ void tick(TimePoint now); // periodic maintenance
58
+
59
+ // -- our own lookups --------------------------------------------------------
60
+ void bootstrap(const std::vector<Address>& nodes, TimePoint now);
61
+ // Remember the (already resolved, numeric) seed set so an internal reseed — e.g. the
62
+ // spider crawl when its frontier empties — can reuse it instead of unresolved hostnames.
63
+ void set_bootstrap_nodes(std::vector<Address> nodes) { bootstrap_nodes_ = std::move(nodes); }
64
+ void find_peers(const InfoHash& info_hash, FindPeers::PeersCallback on_peers,
65
+ FindPeers::DoneCallback on_done, TimePoint now);
66
+ void announce_peer(const InfoHash& info_hash, uint16_t port, bool implied_port,
67
+ FindPeers::PeersCallback on_peers, FindPeers::DoneCallback on_done, TimePoint now);
68
+ void cancel_lookup(const InfoHash& info_hash); // drop active lookups for it
69
+ bool lookup_active(const InfoHash& info_hash, bool announce) const; // is one running?
70
+ std::size_t lookup_count(bool announce) const; // how many running
71
+
72
+ #ifdef RATS_SEARCH_FEATURES
73
+ // Spider mode — aggressive crawl that collects announce_peer requests from the
74
+ // network. set/walk/clear must run on the loop thread (post them); the on/off and
75
+ // ignore flags are atomics readable from anywhere.
76
+ // Just flips the atomic (safe from any thread); spider_walk seeds the pool lazily.
77
+ void set_spider_mode(bool enable) noexcept { spider_mode_.store(enable); }
78
+ bool is_spider_mode() const noexcept { return spider_mode_.load(); }
79
+ void set_spider_ignore(bool ignore) noexcept { spider_ignore_.store(ignore); }
80
+ bool is_spider_ignoring() const noexcept { return spider_ignore_.load(); }
81
+ void set_spider_announce_callback(SpiderAnnounceCallback cb) { spider_announce_ = std::move(cb); }
82
+ void spider_walk(TimePoint now);
83
+ void clear_spider_state();
84
+ std::size_t spider_pool_size() const noexcept { return spider_pool_.size(); }
85
+ std::size_t spider_visited_count() const noexcept { return spider_visited_.size(); }
86
+ #endif
87
+
88
+ // -- accessors / config -----------------------------------------------------
89
+ const NodeId& self() const noexcept { return self_; }
90
+ bool is_ipv6() const noexcept { return ipv6_; }
91
+ void set_external_ip(const IpAddress& ip); // BEP 42: re-derive our id
92
+ const IpAddress& external_address() const noexcept { return external_address_; }
93
+ RoutingTable& routing_table() noexcept { return table_; }
94
+ const RoutingTable& routing_table() const noexcept { return table_; }
95
+ std::size_t active_lookups() const noexcept { return lookups_.size(); }
96
+
97
+ static std::vector<HostEndpoint> default_bootstrap_nodes();
98
+
99
+ private:
100
+ // incoming query handlers (server side)
101
+ void handle_query(const KrpcMessage& msg, const Address& from, TimePoint now);
102
+ void handle_response(const KrpcMessage& msg, const Address& from, TimePoint now);
103
+
104
+ void send_message(const KrpcMessage& msg, const Address& to); // a response (echoes txn, stamps ip)
105
+ void ping(const NodeId& id, const Address& ep, TimePoint now); // liveness probe
106
+ void maybe_update_external_ip(const IpAddress& reported_ip, const Address& from);
107
+ FindPeers& start_lookup(std::unique_ptr<FindPeers> lookup, TimePoint now);
108
+ void reap_finished();
109
+ std::vector<std::string> want() const { return {ipv6_ ? "n6" : "n4"}; }
110
+
111
+ Transport& transport_;
112
+ NodeId self_;
113
+ bool ipv6_;
114
+ RoutingTable table_;
115
+ RpcManager rpc_;
116
+ PeerStore peers_;
117
+ TokenManager tokens_;
118
+ DosBlocker dos_;
119
+ std::vector<std::unique_ptr<Traversal>> lookups_;
120
+ std::vector<Address> bootstrap_nodes_; // resolved seeds, reused by an internal reseed
121
+
122
+ // BEP 42: learn our external address from the "ip" nodes echo back, but require a
123
+ // consensus of distinct responders before trusting it (one node can't poison us).
124
+ static constexpr int kExternalIpVoteThreshold = 5;
125
+ IpAddress external_address_;
126
+ std::map<IpAddress, int> ip_votes_;
127
+ std::set<IpAddress> ip_voters_;
128
+
129
+ // maintenance cadence
130
+ static constexpr std::chrono::seconds kRefreshInterval{6};
131
+ static constexpr std::chrono::minutes kSelfRefreshInterval{6};
132
+ static constexpr std::chrono::seconds kStateLogInterval{30}; // DEBUG heartbeat cadence
133
+ TimePoint last_refresh_{};
134
+ TimePoint last_self_refresh_{};
135
+ TimePoint last_state_log_{};
136
+
137
+ // The most recent model time, stamped at every entry point (on_datagram / tick /
138
+ // start_lookup / bootstrap). Lets a lookup's done-callback measure its own duration
139
+ // without the engine threading `now` through every callback signature.
140
+ TimePoint now_{};
141
+
142
+ #ifdef RATS_SEARCH_FEATURES
143
+ // An id that looks close to `target` (first 10 bytes from target, last 10 from us),
144
+ // so peers route more queries our way during a crawl.
145
+ NodeId neighbor_id(const NodeId& target) const;
146
+ void add_spider_node(const NodeEntry& node);
147
+ void spider_absorb(const KrpcMessage& msg, const Address& from); // reply → spider pool
148
+ bool spider_contacted(const IpAddress& ip) const;
149
+
150
+ std::atomic<bool> spider_mode_{false};
151
+ std::atomic<bool> spider_ignore_{false};
152
+ SpiderAnnounceCallback spider_announce_;
153
+ std::vector<NodeEntry> spider_pool_; // crawl frontier (kept apart from the routing table)
154
+ std::unordered_set<NodeId, NodeIdHash> spider_visited_;
155
+ std::unordered_set<IpAddress> spider_contacted_ips_;
156
+ TimePoint last_spider_bootstrap_{};
157
+ static constexpr std::size_t kMaxSpiderNodes = 2000;
158
+ static constexpr std::size_t kMaxSpiderVisited = 10000;
159
+ static constexpr std::size_t kMaxSpiderContactedIps = 10000;
160
+ #endif
161
+ };
162
+
163
+ } // namespace dht
164
+ } // namespace librats