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,185 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file routing_table.h
5
+ * @brief A Kademlia routing table with libtorrent-style dynamic buckets.
6
+ *
7
+ * Pure in-memory state — no sockets, no locks. It lives entirely on the DHT actor
8
+ * thread, so it never synchronises. It also never sends anything itself: liveness
9
+ * pings are issued by the owning Node, which reports the outcome back through
10
+ * node_seen() (it replied) / node_failed() (it timed out). That keeps the table a
11
+ * testable data structure and the I/O in one place.
12
+ *
13
+ * Buckets are an ordered list rooted at our own id: buckets_[0] is the *farthest*
14
+ * region of the keyspace and buckets_.back() the *closest* catch-all. When the last
15
+ * bucket overflows and a split would actually separate its contacts, a new bucket is
16
+ * appended and the contacts re-homed. Buckets near the top are larger (the extended
17
+ * routing table) because that is where the keyspace — and the first hop of almost any
18
+ * lookup — is densest, so more contacts there means fewer hops.
19
+ *
20
+ * Each bucket holds a *live* set — the contacts we route with, up to its size limit —
21
+ * plus a small *replacement cache* of standbys promoted the moment a live contact
22
+ * dies. Contact quality (RTT, failures, BEP 42 verification) lives on the NodeEntry
23
+ * and drives who gets kept, refreshed, or evicted, while a sub-prefix "spread" rule
24
+ * keeps a full bucket covering as many sub-branches as possible.
25
+ *
26
+ * Sybil/eclipse resistance (BEP 42). Admission is IP-diversity limited so no single
27
+ * operator can pack the table with contacts it controls and hijack lookups:
28
+ * 1. at most one contact per IP across the whole table, and
29
+ * 2. at most one contact per /24 (IPv4) or /64 (IPv6) within any single bucket.
30
+ * Only *public* addresses are constrained — private/loopback/CGNAT IPs can't be the
31
+ * source of a real Sybil attack and would wrongly collide on a LAN (this mirrors the
32
+ * BEP 42 exemption already applied by verify_node_id_for_ip). A per-IP count index
33
+ * (ip_count_) makes rule 1 an O(1) check; rule 2 is a small per-bucket scan.
34
+ */
35
+
36
+ #include "librats/core/address.h"
37
+ #include "librats/dht/id.h"
38
+ #include "librats/dht/node_entry.h"
39
+
40
+ #include <chrono>
41
+ #include <cstddef>
42
+ #include <optional>
43
+ #include <string>
44
+ #include <unordered_map>
45
+ #include <vector>
46
+
47
+ namespace librats {
48
+ namespace dht {
49
+
50
+ class RoutingTable {
51
+ public:
52
+ // Consecutive failures tolerated before a contact with no standby is dropped.
53
+ static constexpr uint8_t kMaxFailCount = 5;
54
+
55
+ // `extended` enables the larger top buckets (the extended routing table). On by
56
+ // default for production; tests that want uniform k-sized buckets pass false.
57
+ explicit RoutingTable(const NodeId& self, bool extended = true);
58
+
59
+ // -- insertion --------------------------------------------------------------
60
+
61
+ // We learned this node exists from someone else's reply. Recorded as unpinged
62
+ // (a candidate to verify); it can fill a live slot but never splits or evicts a
63
+ // confirmed contact. `verified` is the caller's BEP 42 verdict on the (id, ip)
64
+ // pair, used only as a tie-breaker between unpinged candidates.
65
+ void heard_about(const NodeId& id, const Address& endpoint, bool verified = false);
66
+
67
+ // This node replied to us: recorded as confirmed with its RTT. A confirmed
68
+ // contact may split a full bucket or displace a weaker one, but a healthy contact
69
+ // alone in its sub-prefix slot is only removed via node_failed(). Returns true if
70
+ // it ended up live.
71
+ bool node_seen(const NodeId& id, const Address& endpoint,
72
+ uint16_t rtt = NodeEntry::kRttUnknown, bool verified = false);
73
+
74
+ // A query to this node timed out. Since this only fires after a real liveness
75
+ // check failed, the contact is replaced immediately if a standby is waiting;
76
+ // otherwise it's kept until it exhausts kMaxFailCount, then dropped.
77
+ void node_failed(const NodeId& id, const Address& endpoint);
78
+
79
+ // -- queries ----------------------------------------------------------------
80
+
81
+ // Up to `count` contacts closest to `target` (XOR metric), closest first. By
82
+ // default only confirmed contacts are returned (for answering queries); pass
83
+ // include_unconfirmed=true to also seed our own lookups from unpinged contacts.
84
+ std::vector<NodeEntry> find_closest(const NodeId& target, std::size_t count = kBucketSize,
85
+ bool include_unconfirmed = false) const;
86
+
87
+ // The live contact most in need of a liveness check: the least-recently-touched
88
+ // (a never-contacted contact sorts first). The chosen contact is stamped as touched
89
+ // at `now`, so successive refreshes rotate through different contacts instead of
90
+ // re-probing this one while its probe is still outstanding. nullopt when there are
91
+ // no live contacts.
92
+ std::optional<NodeEntry> next_to_refresh(std::chrono::steady_clock::time_point now);
93
+
94
+ std::size_t size() const; // number of live contacts
95
+ bool empty() const { return size() == 0; }
96
+ int bucket_count() const noexcept { return static_cast<int>(buckets_.size()); }
97
+
98
+ // A pretty, multi-line snapshot for periodic DEBUG logging: a header plus one row
99
+ // per bucket with its live/limit fill bar and best & worst contact. Pure formatting,
100
+ // no I/O — built only when actually logged (callers guard on the log level).
101
+ std::string describe() const;
102
+
103
+ // -- node id changes (BEP 42) ----------------------------------------------
104
+
105
+ const NodeId& self() const noexcept { return self_; }
106
+ void set_self(const NodeId& id); // re-buckets every contact against the new id
107
+
108
+ // -- persistence ------------------------------------------------------------
109
+
110
+ std::vector<NodeEntry> good_contacts() const; // confirmed live contacts, to save
111
+ void load_contacts(const std::vector<NodeEntry>& contacts); // bulk restore, preserving quality
112
+
113
+ // -- diagnostics ------------------------------------------------------------
114
+
115
+ // True iff the per-IP index (ip_count_) matches a fresh count over the buckets.
116
+ // Cheap invariant used by tests to prove the IP accounting stays balanced across
117
+ // arbitrary add/evict/fail churn; not needed at runtime.
118
+ bool ip_index_consistent() const;
119
+
120
+ private:
121
+ struct Bucket {
122
+ std::vector<NodeEntry> live; // active contacts, up to bucket_limit(index)
123
+ std::vector<NodeEntry> replacements; // standbys, up to kBucketSize
124
+ };
125
+
126
+ // The bucket `id` belongs to: its shared-prefix length with us, capped to the
127
+ // last (catch-all) bucket. buckets_ is never empty, so this is always valid.
128
+ int bucket_index(const NodeId& id) const noexcept;
129
+ // Live-set size limit for a bucket by position: larger near the top when the
130
+ // extended table is on, otherwise a flat kBucketSize.
131
+ int bucket_limit(int index) const noexcept;
132
+
133
+ // The one insertion path. `e` carries its own quality (pinged/confirmed via
134
+ // fail_count). Returns true if it ended up in a live set.
135
+ bool add_node(NodeEntry e);
136
+
137
+ // -- IP-diversity admission (Sybil/eclipse resistance) ----------------------
138
+
139
+ // May a genuinely new (not-already-known-by-id) contact `e` enter bucket `idx`?
140
+ // Enforces the two rules above for public IPs only; a same-endpoint contact that
141
+ // has quietly changed its id is evicted here as a poisoning signal. Non-public
142
+ // addresses are always admissible. Rare collision handling lives in this one place.
143
+ bool passes_ip_diversity(Bucket& b, int idx, const NodeEntry& e);
144
+
145
+ // Locate the contact currently at endpoint `ep` (ip+port) anywhere in the table.
146
+ // Only walked on a public-IP collision, so the O(table) cost is off the hot path.
147
+ struct Located { int bucket; bool live; std::size_t index; };
148
+ std::optional<Located> find_by_endpoint(const Address& ep);
149
+
150
+ // Keep ip_count_ in lock-step with the buckets. The invariant is simple and local:
151
+ // every push into a live/replacement set calls ip_track, every removal ip_untrack,
152
+ // and an in-place overwrite goes through assign() (untrack old + track new). Moves
153
+ // between the two sets are therefore self-balancing. Only public IPs are indexed.
154
+ void ip_track(const IpAddress& ip);
155
+ void ip_untrack(const IpAddress& ip);
156
+ void assign(NodeEntry& slot, const NodeEntry& e); // overwrite a slot, keeping the index synced
157
+
158
+ // May the (full, last) bucket at `index` be split to fit a confirmed `e`?
159
+ bool can_split(int index, const NodeEntry& e) const;
160
+ // Append a bucket and re-home the old last bucket's contacts by shared prefix.
161
+ void split_bucket();
162
+ // Bucket `b` (index `index`, limit `limit`) is full and `e` is confirmed: free a
163
+ // slot by evicting the least valuable node while preserving sub-prefix spread.
164
+ bool replace_for_spread(Bucket& b, int index, int limit, const NodeEntry& e);
165
+ // Add/refresh a standby, evicting the worst one if the cache is full.
166
+ void stash_replacement(Bucket& b, const NodeEntry& entry);
167
+ // Spill live-set overflow into replacements and cap the cache (after a split).
168
+ void trim_to_limit(int index);
169
+ // Promote the best standbys into any free live slots a split opened up, so a
170
+ // parked contact isn't stuck (replacements are never pinged or refreshed).
171
+ void fill_from_replacements(int index);
172
+ // Drop trailing empty buckets, but always keep at least one.
173
+ void prune_empty_back();
174
+
175
+ NodeId self_;
176
+ bool extended_;
177
+ std::vector<Bucket> buckets_; // [0] = farthest, back() = closest catch-all
178
+
179
+ // Count of live+replacement contacts per *public* IP across the whole table. Backs
180
+ // rule 1 (one contact per IP) as an O(1) lookup; kept balanced by ip_track/untrack.
181
+ std::unordered_map<IpAddress, int> ip_count_;
182
+ };
183
+
184
+ } // namespace dht
185
+ } // namespace librats
@@ -0,0 +1,127 @@
1
+ #include "librats/dht/rpc_manager.h"
2
+ #include "librats/dht/krpc.h"
3
+ #include "librats/dht/log.h"
4
+
5
+ #include <vector>
6
+
7
+ namespace librats {
8
+ namespace dht {
9
+
10
+ namespace {
11
+
12
+ // Transaction ids are 2-byte strings on the wire.
13
+ std::string txn_to_string(uint16_t t) {
14
+ std::string s(2, '\0');
15
+ s[0] = static_cast<char>((t >> 8) & 0xff);
16
+ s[1] = static_cast<char>(t & 0xff);
17
+ return s;
18
+ }
19
+
20
+ bool string_to_txn(const std::string& s, uint16_t& out) {
21
+ if (s.size() != 2) return false;
22
+ out = static_cast<uint16_t>((static_cast<uint8_t>(s[0]) << 8) | static_cast<uint8_t>(s[1]));
23
+ return true;
24
+ }
25
+
26
+ } // namespace
27
+
28
+ uint16_t RpcManager::next_txn() {
29
+ // Random, unpredictable transaction ids. A counter is trivially predictable, so an off-path
30
+ // attacker could forge replies to our queries; with a random id it must guess a
31
+ // 16-bit value *and* spoof the exact endpoint we queried (handle_response checks
32
+ // both). pending_ is keyed by txn, so re-draw on the rare collision with an id
33
+ // still in flight — 65536 ids vastly exceed what we ever have outstanding.
34
+ std::uniform_int_distribution<uint32_t> draw(0, 0xffff);
35
+ for (int i = 0; i < 65536; ++i) {
36
+ const uint16_t t = static_cast<uint16_t>(draw(rng_));
37
+ if (pending_.find(t) == pending_.end()) return t;
38
+ }
39
+ return static_cast<uint16_t>(draw(rng_)); // table full — unreachable in practice
40
+ }
41
+
42
+ bool RpcManager::invoke(KrpcMessage& msg, const Address& to, const ObserverPtr& obs, TimePoint now) {
43
+ const uint16_t t = next_txn();
44
+ msg.transaction_id = txn_to_string(t);
45
+
46
+ const std::vector<uint8_t> data = KrpcProtocol::encode_message(msg);
47
+ if (data.empty()) return false;
48
+
49
+ transport_.send(to, data);
50
+ obs->txn_ = t;
51
+ pending_[t] = Pending{obs, to, now};
52
+ LOG_DEBUG("dht.rpc", "→ " << KrpcProtocol::query_type_to_string(msg.query_type)
53
+ << " to " << to.to_string() << " [txn " << t << "]");
54
+ return true;
55
+ }
56
+
57
+ void RpcManager::send_oneshot(KrpcMessage& msg, const Address& to) {
58
+ msg.transaction_id = txn_to_string(next_txn()); // the peer may echo it; we don't track the reply
59
+ const std::vector<uint8_t> data = KrpcProtocol::encode_message(msg);
60
+ if (!data.empty()) {
61
+ transport_.send(to, data);
62
+ LOG_DEBUG("dht.rpc", "→ " << KrpcProtocol::query_type_to_string(msg.query_type)
63
+ << " to " << to.to_string() << " (oneshot)");
64
+ }
65
+ }
66
+
67
+ bool RpcManager::handle_response(const KrpcMessage& msg, const Address& from, TimePoint now) {
68
+ uint16_t t;
69
+ if (!string_to_txn(msg.transaction_id, t)) return false;
70
+
71
+ auto it = pending_.find(t);
72
+ if (it == pending_.end()) return false; // unknown / already resolved
73
+ if (it->second.endpoint != from) { // anti-spoof: not from whom we queried
74
+ LOG_DEBUG("dht.rpc", "dropped reply [txn " << t << "]: from " << from.to_string()
75
+ << " != queried " << it->second.endpoint.to_string());
76
+ return false;
77
+ }
78
+
79
+ const ObserverPtr obs = it->second.obs; // keep alive across the callback
80
+ const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it->second.sent).count();
81
+ const uint16_t rtt = elapsed < 0 ? 0 : (elapsed > 0xfffe ? uint16_t(0xfffe) : uint16_t(elapsed));
82
+ pending_.erase(it);
83
+
84
+ if (msg.type == KrpcMessageType::Error) {
85
+ // A peer that explicitly rejected us is alive but refused — a different signal from
86
+ // silence, so log it distinctly even though it drives the same failed-query path.
87
+ LOG_DEBUG("dht.rpc", "<- error from " << from.to_string() << " [txn " << t << "]: "
88
+ << static_cast<int>(msg.error_code) << ' ' << msg.error_message);
89
+ obs->on_timeout(now); // an error is a failed query
90
+ } else {
91
+ // The success half of the → query pair, carrying the round-trip we just measured.
92
+ LOG_DEBUG("dht.rpc", "<- response from " << from.to_string() << " (" << short_hex(msg.response_id)
93
+ << ") [txn " << t << "] " << rtt << "ms");
94
+ obs->on_response(msg, rtt, now);
95
+ }
96
+ return true;
97
+ }
98
+
99
+ void RpcManager::tick(TimePoint now) {
100
+ // Collect first (callbacks mutate pending_), and keep the observers alive while we call them.
101
+ std::vector<ObserverPtr> shorts, fulls;
102
+ for (auto& [t, p] : pending_) {
103
+ const auto elapsed = now - p.sent;
104
+ if (elapsed >= kFullTimeout)
105
+ fulls.push_back(p.obs);
106
+ else if (elapsed >= kShortTimeout && !p.obs->has(Observer::kShortTimeout))
107
+ shorts.push_back(p.obs);
108
+ }
109
+
110
+ for (auto& o : shorts) o->on_short_timeout(now); // keeps the pending entry
111
+ for (auto& o : fulls) {
112
+ // Only the *full* timeout is logged (a query truly gave up) — short timeouts just
113
+ // widen the branch factor and fire constantly, so they stay silent.
114
+ LOG_DEBUG("dht.rpc", "timeout " << short_hex(o->id()) << ' ' << o->endpoint().to_string());
115
+ cancel(o.get());
116
+ o->on_timeout(now);
117
+ }
118
+ }
119
+
120
+ void RpcManager::cancel(Observer* obs) {
121
+ if (!obs) return;
122
+ auto it = pending_.find(obs->txn_);
123
+ if (it != pending_.end() && it->second.obs.get() == obs) pending_.erase(it);
124
+ }
125
+
126
+ } // namespace dht
127
+ } // namespace librats
@@ -0,0 +1,77 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file rpc_manager.h
5
+ * @brief Tracks outstanding DHT queries: transaction ids, timeouts, anti-spoofing.
6
+ *
7
+ * One unified place for every query the node sends — lookups, liveness pings,
8
+ * bootstrap — each represented by an Observer. invoke() stamps a transaction id,
9
+ * encodes and sends the message, and remembers it; handle_response() matches a reply
10
+ * back to its observer (only if it came from the very endpoint we queried) and
11
+ * dispatches it; tick() ages out the silent ones with a two-level timeout.
12
+ *
13
+ * Single-threaded actor: no locks, and time is passed in rather than read from a
14
+ * clock, so behaviour is fully deterministic and testable.
15
+ */
16
+
17
+ #include "librats/core/address.h"
18
+ #include "librats/dht/observer.h"
19
+ #include "librats/dht/transport.h"
20
+
21
+ #include <chrono>
22
+ #include <cstddef>
23
+ #include <cstdint>
24
+ #include <random>
25
+ #include <unordered_map>
26
+
27
+ namespace librats {
28
+ struct KrpcMessage;
29
+
30
+ namespace dht {
31
+
32
+ class RpcManager {
33
+ public:
34
+ // After the short timeout we free the query's concurrency slot but keep waiting;
35
+ // after the full timeout we give up on it.
36
+ static constexpr std::chrono::milliseconds kShortTimeout{2000};
37
+ static constexpr std::chrono::milliseconds kFullTimeout{15000};
38
+
39
+ explicit RpcManager(Transport& transport)
40
+ : transport_(transport), rng_(std::random_device{}()) {}
41
+
42
+ // Stamp a transaction id on `msg`, send it to `to`, and register `obs` to receive
43
+ // the outcome. Returns false if the message could not be encoded.
44
+ bool invoke(KrpcMessage& msg, const Address& to, const ObserverPtr& obs, TimePoint now);
45
+
46
+ // Send a query we don't track the reply to (e.g. announce_peer). Fire-and-forget.
47
+ void send_oneshot(KrpcMessage& msg, const Address& to);
48
+
49
+ // Route a reply/error back to its observer. Returns true only if it matched an
50
+ // outstanding query AND arrived from the endpoint we queried (anti-spoofing);
51
+ // unmatched or spoofed replies are ignored.
52
+ bool handle_response(const KrpcMessage& msg, const Address& from, TimePoint now);
53
+
54
+ // Fire short/full timeouts for queries that have gone quiet. Call periodically.
55
+ void tick(TimePoint now);
56
+
57
+ // Forget the in-flight query for `obs`, if any (the traversal is abandoning it).
58
+ void cancel(Observer* obs);
59
+
60
+ std::size_t outstanding() const noexcept { return pending_.size(); }
61
+
62
+ private:
63
+ struct Pending {
64
+ ObserverPtr obs;
65
+ Address endpoint;
66
+ TimePoint sent;
67
+ };
68
+
69
+ uint16_t next_txn();
70
+
71
+ Transport& transport_;
72
+ std::unordered_map<uint16_t, Pending> pending_;
73
+ std::mt19937 rng_; // seeds unpredictable transaction ids (anti-spoofing)
74
+ };
75
+
76
+ } // namespace dht
77
+ } // namespace librats
@@ -0,0 +1,92 @@
1
+ #include "librats/dht/storage.h"
2
+ #include "librats/crypto/sha1.h"
3
+
4
+ #include <algorithm>
5
+ #include <random>
6
+
7
+ namespace librats {
8
+ namespace dht {
9
+
10
+ // ---- TokenManager ----------------------------------------------------------
11
+
12
+ void TokenManager::maybe_rotate(TimePointSys now) {
13
+ if (!initialized_) {
14
+ std::random_device rd;
15
+ for (auto& b : secret_) b = static_cast<uint8_t>(rd());
16
+ prev_secret_ = secret_;
17
+ rotated_at_ = now;
18
+ initialized_ = true;
19
+ return;
20
+ }
21
+ if (now - rotated_at_ >= kRotateInterval) {
22
+ prev_secret_ = secret_;
23
+ std::random_device rd;
24
+ for (auto& b : secret_) b = static_cast<uint8_t>(rd());
25
+ rotated_at_ = now;
26
+ }
27
+ }
28
+
29
+ std::string TokenManager::compute(const Address& querier, const InfoHash& info_hash,
30
+ const std::array<uint8_t, 16>& secret) const {
31
+ // Port is deliberately excluded — a NAT may show a different source port on the
32
+ // follow-up announce, but the address stays the same.
33
+ const ByteView ip = querier.ip.bytes();
34
+ std::vector<uint8_t> data;
35
+ data.reserve(secret.size() + ip.size() + info_hash.size());
36
+ data.insert(data.end(), secret.begin(), secret.end());
37
+ data.insert(data.end(), ip.begin(), ip.end());
38
+ data.insert(data.end(), info_hash.begin(), info_hash.end());
39
+ return SHA1::hash_bytes(data);
40
+ }
41
+
42
+ std::string TokenManager::generate(const Address& querier, const InfoHash& info_hash, TimePointSys now) {
43
+ maybe_rotate(now);
44
+ return compute(querier, info_hash, secret_);
45
+ }
46
+
47
+ bool TokenManager::verify(const Address& querier, const InfoHash& info_hash,
48
+ const std::string& token, TimePointSys now) {
49
+ if (token.empty()) return false;
50
+ maybe_rotate(now);
51
+ return token == compute(querier, info_hash, secret_)
52
+ || token == compute(querier, info_hash, prev_secret_);
53
+ }
54
+
55
+ // ---- PeerStore -------------------------------------------------------------
56
+
57
+ void PeerStore::store(const InfoHash& info_hash, const Address& peer, TimePointSys now) {
58
+ auto& peers = table_[info_hash];
59
+ for (auto& e : peers) {
60
+ if (e.peer == peer) { // already known → just refresh its timestamp
61
+ e.added = now;
62
+ return;
63
+ }
64
+ }
65
+ peers.push_back({peer, now});
66
+ if (peers.size() > kMaxPeersPerHash) peers.erase(peers.begin()); // drop the oldest
67
+ }
68
+
69
+ std::vector<Address> PeerStore::get(const InfoHash& info_hash, std::size_t max) const {
70
+ std::vector<Address> out;
71
+ auto it = table_.find(info_hash);
72
+ if (it == table_.end()) return out;
73
+ for (const auto& e : it->second) {
74
+ out.push_back(e.peer);
75
+ if (out.size() >= max) break;
76
+ }
77
+ return out;
78
+ }
79
+
80
+ void PeerStore::expire(TimePointSys now) {
81
+ for (auto it = table_.begin(); it != table_.end();) {
82
+ auto& peers = it->second;
83
+ peers.erase(std::remove_if(peers.begin(), peers.end(),
84
+ [&](const Entry& e) { return now - e.added > kPeerTtl; }),
85
+ peers.end());
86
+ if (peers.empty()) it = table_.erase(it);
87
+ else ++it;
88
+ }
89
+ }
90
+
91
+ } // namespace dht
92
+ } // namespace librats
@@ -0,0 +1,74 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file storage.h
5
+ * @brief Server-side DHT state: announce write-tokens and the announced-peer table (BEP 5).
6
+ *
7
+ * Both are pure data driven by the node's incoming-query handlers, with time passed
8
+ * in so they're deterministic and lock-free (single actor thread).
9
+ */
10
+
11
+ #include "librats/core/address.h"
12
+ #include "librats/dht/id.h"
13
+
14
+ #include <array>
15
+ #include <chrono>
16
+ #include <cstddef>
17
+ #include <cstdint>
18
+ #include <map>
19
+ #include <string>
20
+ #include <vector>
21
+
22
+ namespace librats {
23
+ namespace dht {
24
+
25
+ using TimePointSys = std::chrono::steady_clock::time_point;
26
+
27
+ // Write tokens (BEP 5). A token is handed out with every get_peers reply and must be
28
+ // presented back on announce_peer — proving the announcer can actually receive at the
29
+ // address it claims. token = SHA1(secret || querier_ip || info_hash): unforgeable
30
+ // (the secret is private) and bound to both the address and the info-hash, so it can't
31
+ // be replayed for a different peer or torrent. The secret rotates periodically; the
32
+ // previous one stays valid across a single rotation so tokens don't expire abruptly.
33
+ class TokenManager {
34
+ public:
35
+ static constexpr std::chrono::minutes kRotateInterval{5};
36
+
37
+ std::string generate(const Address& querier, const InfoHash& info_hash, TimePointSys now);
38
+ bool verify(const Address& querier, const InfoHash& info_hash,
39
+ const std::string& token, TimePointSys now);
40
+
41
+ private:
42
+ void maybe_rotate(TimePointSys now);
43
+ std::string compute(const Address& querier, const InfoHash& info_hash,
44
+ const std::array<uint8_t, 16>& secret) const;
45
+
46
+ std::array<uint8_t, 16> secret_{};
47
+ std::array<uint8_t, 16> prev_secret_{};
48
+ TimePointSys rotated_at_{};
49
+ bool initialized_ = false;
50
+ };
51
+
52
+ // The peers that announced themselves under each info-hash, so we can answer
53
+ // get_peers. Peers expire after kPeerTtl; each info-hash is capped to bound memory.
54
+ class PeerStore {
55
+ public:
56
+ static constexpr std::chrono::minutes kPeerTtl{30};
57
+ static constexpr std::size_t kMaxPeersPerHash = 128;
58
+
59
+ void store(const InfoHash& info_hash, const Address& peer, TimePointSys now);
60
+ std::vector<Address> get(const InfoHash& info_hash, std::size_t max = kMaxPeersPerHash) const;
61
+ void expire(TimePointSys now);
62
+
63
+ std::size_t hash_count() const noexcept { return table_.size(); }
64
+
65
+ private:
66
+ struct Entry {
67
+ Address peer;
68
+ TimePointSys added;
69
+ };
70
+ std::map<InfoHash, std::vector<Entry>> table_;
71
+ };
72
+
73
+ } // namespace dht
74
+ } // namespace librats
@@ -0,0 +1,27 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file transport.h
5
+ * @brief The outbound datagram sink the DHT sends through.
6
+ *
7
+ * The real implementation (Phase 5) owns a UDP socket; tests substitute a mock that
8
+ * just records what was sent. Sending is fire-and-forget and must never block — the
9
+ * whole DHT runs on one actor thread.
10
+ */
11
+
12
+ #include "librats/core/address.h"
13
+
14
+ #include <cstdint>
15
+ #include <vector>
16
+
17
+ namespace librats {
18
+ namespace dht {
19
+
20
+ class Transport {
21
+ public:
22
+ virtual ~Transport() = default;
23
+ virtual void send(const Address& to, const std::vector<uint8_t>& datagram) = 0;
24
+ };
25
+
26
+ } // namespace dht
27
+ } // namespace librats