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,326 @@
1
+ #include "librats/dht/traversal.h"
2
+ #include "librats/dht/bep42.h"
3
+ #include "librats/dht/krpc.h"
4
+ #include "librats/dht/log.h"
5
+
6
+ #include <algorithm>
7
+ #include <atomic>
8
+
9
+ // Per-wave "dht.traversal" round logging is verbose; gate it behind a compile-time flag
10
+ // so it can be dropped entirely from a build. Default on (true).
11
+ #ifndef DHT_TRAVERSAL_DEBUG
12
+ #define DHT_TRAVERSAL_DEBUG true
13
+ #endif
14
+
15
+ namespace librats {
16
+ namespace dht {
17
+
18
+ namespace {
19
+ // Monotonic lookup-id source. Atomic because v4 and v6 nodes run on separate threads,
20
+ // so ids stay unique across both instances — a single "L<id>" disambiguates everything.
21
+ // Unsigned so wrap-around past 2^32 is defined (benign) rather than signed-overflow UB.
22
+ std::atomic<unsigned> g_lookup_seq{0};
23
+ } // namespace
24
+
25
+ Traversal::Traversal(RoutingTable& table, RpcManager& rpc, const NodeId& self, const NodeId& target)
26
+ : table_(table), rpc_(rpc), self_(self), target_(target),
27
+ id_(g_lookup_seq.fetch_add(1, std::memory_order_relaxed)) {}
28
+
29
+ Traversal::~Traversal() {
30
+ // Detach any still-in-flight queries so the RpcManager won't call into a
31
+ // half-destroyed traversal once we're gone.
32
+ for (auto& obs : results_) {
33
+ if (obs->has(Observer::kQueried) && !obs->has(Observer::kAlive) && !obs->has(Observer::kFailed))
34
+ rpc_.cancel(obs.get());
35
+ }
36
+ }
37
+
38
+ void Traversal::start(TimePoint now) {
39
+ // Seed from the closest contacts we know, including unconfirmed ones so a fresh
40
+ // table can still bootstrap a lookup.
41
+ for (const auto& n : table_.find_closest(target_, kBucketSize, /*include_unconfirmed=*/true))
42
+ add_entry(n.id, n.endpoint);
43
+ if (add_requests(now)) finish();
44
+ }
45
+
46
+ void Traversal::add_seed(const Address& ep) {
47
+ add_entry(NodeId{}, ep, Observer::kInitial);
48
+ }
49
+
50
+ void Traversal::add_entry(const NodeId& id, const Address& ep, uint8_t flags) {
51
+ if (done_) return;
52
+
53
+ // Seeds with an unknown id (routers) go to the unsorted tail: we can't place them
54
+ // by distance and there may be several, so we skip the id-based dedup for them.
55
+ if (flags & Observer::kInitial) {
56
+ ObserverPtr seed = make_observer(id, ep);
57
+ seed->set(Observer::kInitial);
58
+ results_.push_back(std::move(seed));
59
+ return;
60
+ }
61
+
62
+ // Binary-search the sorted prefix results_[0..sorted_) for the insertion point.
63
+ // A duplicate id has identical XOR distance to the target, so lower_bound lands
64
+ // exactly on it — one search does both the dedup and the placement.
65
+ const auto sorted_end = results_.begin() + sorted_;
66
+ auto pos = std::lower_bound(results_.begin(), sorted_end, id,
67
+ [&](const ObserverPtr& o, const NodeId& nid) { return closer_to(o->id(), nid, target_); });
68
+ if (pos != sorted_end && (*pos)->id() == id) return; // already a candidate
69
+
70
+ // Anti-Sybil (libtorrent's dht_restrict_search_ips): a genuinely new candidate — the
71
+ // dedup above already let same-id repeats out — sharing a /24 (v4) or /64 (v6) with one
72
+ // we've already admitted is ignored, so no single operator can pack the candidate set
73
+ // and steer the search. The first node in a block wins. This mirrors libtorrent, which
74
+ // runs the check *after* the id dedup and skips it for kInitial seeds (handled above).
75
+ // The packed prefixes live in a per-lookup set, so it is an O(1) test rather than an
76
+ // O(n) scan of results_, and a prefix persists for the lookup's lifetime (a candidate
77
+ // dropped by truncation can't re-open its block). Only public IPs are restricted, so a
78
+ // LAN/loopback/CGNAT test topology is unaffected.
79
+ if (register_subnet(ep)) return;
80
+
81
+ results_.insert(pos, make_observer(id, ep));
82
+ ++sorted_;
83
+
84
+ // Cap the candidate list; abandon anything past the cutoff, releasing in-flight
85
+ // queries so they neither leak nor report back later.
86
+ if (results_.size() > kMaxResults) {
87
+ for (std::size_t i = kMaxResults; i < results_.size(); ++i) {
88
+ Observer* o = results_[i].get();
89
+ const bool in_flight = o->has(Observer::kQueried) && !o->has(Observer::kAlive) &&
90
+ !o->has(Observer::kFailed) && !o->has(Observer::kDone);
91
+ if (in_flight) {
92
+ o->set(Observer::kDone);
93
+ if (invoke_count_ > 0) --invoke_count_;
94
+ if (o->has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
95
+ --branch_factor_;
96
+ rpc_.cancel(o);
97
+ }
98
+ }
99
+ results_.resize(kMaxResults);
100
+ if (sorted_ > static_cast<int>(kMaxResults)) sorted_ = static_cast<int>(kMaxResults);
101
+ }
102
+ }
103
+
104
+ bool Traversal::register_subnet(const Address& ep) {
105
+ // Only public addresses are constrained; a LAN/loopback/CGNAT topology is exempt (the
106
+ // same rule the routing table applies) and never collides. is_public_address implies a
107
+ // specified v4/v6 address, so bytes() below has the octets we index.
108
+ if (!is_public_address(ep.ip)) return false;
109
+
110
+ const uint8_t* b = ep.ip.bytes().data(); // network-order octets
111
+ if (ep.ip.is_v6()) {
112
+ // /64: the leading 8 bytes packed big-endian (matches ip_too_close's compare and
113
+ // libtorrent's read_uint64 over the first 8 v6 octets).
114
+ uint64_t prefix = 0;
115
+ for (int i = 0; i < 8; ++i) prefix = (prefix << 8) | b[i];
116
+ return !subnet6_.insert(prefix).second; // insert() fails ⇒ block already admitted
117
+ }
118
+ // /24: the leading 3 bytes, low octet dropped — libtorrent's to_uint() & 0xffffff00.
119
+ const uint32_t prefix = (uint32_t(b[0]) << 24) | (uint32_t(b[1]) << 16) | (uint32_t(b[2]) << 8);
120
+ return !subnet4_.insert(prefix).second;
121
+ }
122
+
123
+ bool Traversal::add_requests(TimePoint now) {
124
+ if (done_) return true;
125
+
126
+ // How many of the k closest still need a live answer before the search has converged.
127
+ // Starts at k and counts down as we meet alive nodes walking the list closest-first;
128
+ // reaching 0 means the k nodes nearest the target have all replied.
129
+ int alive_needed = static_cast<int>(kBucketSize);
130
+ // Queries among the k closest that we still genuinely wait on: sent, not yet answered,
131
+ // and not yet past their short timeout. A query that has already passed its short
132
+ // timeout is deliberately NOT counted here — a stale straggler must not block completion
133
+ // (see the return below). A just-sent query counts too.
134
+ int in_flight = 0;
135
+ int alive = 0; // alive nodes seen among the closest (for the round log line)
136
+ int sent = 0; // queries fired in this pass (a stall sends nothing)
137
+
138
+ for (auto& obs_ptr : results_) {
139
+ if (alive_needed <= 0) break; // the k closest have all replied — stop scanning
140
+ Observer* o = obs_ptr.get();
141
+
142
+ if (o->has(Observer::kAlive)) { --alive_needed; ++alive; continue; }
143
+ if (o->has(Observer::kFailed) || o->has(Observer::kDone)) continue;
144
+ if (o->has(Observer::kQueried)) {
145
+ // Only a *fresh* query — one that hasn't yet passed its short timeout — blocks
146
+ // completion. Once a query is past its short timeout it has almost certainly
147
+ // gone dead; holding the whole lookup for it until the 15 s full timeout is
148
+ // pure dead time. Such a node gave us no peers, and (being silent) no write
149
+ // token, so it was never a candidate to announce to either — waiting for it
150
+ // cannot improve the result. We keep the observer registered so a late reply
151
+ // is still welcome, but a stale query no longer gates convergence.
152
+ if (!o->has(Observer::kShortTimeout)) ++in_flight;
153
+ continue;
154
+ }
155
+
156
+ if (invoke_count_ >= branch_factor_) continue; // no free slot; keep scanning to count
157
+
158
+ o->set(Observer::kQueried);
159
+ if (invoke(obs_ptr, now)) {
160
+ ++invoke_count_;
161
+ ++in_flight;
162
+ ++sent;
163
+ } else {
164
+ o->set(Observer::kFailed);
165
+ }
166
+ }
167
+
168
+ // One line per wave that actually fired queries — this is the search "round". A stall
169
+ // (slots full, nothing sent) prints nothing, so the cadence of these lines reveals
170
+ // timeout-bound progress; `closest` (shared-prefix bits with the target) climbing shows
171
+ // real convergence, while a flat `closest` with a rising `branch` means we're spinning
172
+ // on dead/sybil nodes. Built only when DEBUG is on (the macro guards the level).
173
+ if (sent > 0) {
174
+ ++round_;
175
+ #if DHT_TRAVERSAL_DEBUG
176
+ const int closest = sorted_ > 0 ? shared_prefix_bits(results_.front()->id(), target_) : 0;
177
+ LOG_DEBUG("dht.traversal", name() << " L" << id_ << ' ' << short_hex(target_) << " round "
178
+ << round_ << ": +" << sent << " sent, " << invoke_count_
179
+ << " in-flight, " << alive << '/' << kBucketSize << " alive, branch="
180
+ << branch_factor_ << ", closest +" << closest << "b");
181
+ #endif
182
+ }
183
+
184
+ // Done exactly when nothing we still wait on is in flight. This one check already covers
185
+ // both ways a lookup can end:
186
+ // - success: the k closest have all replied, so none of them is still in flight;
187
+ // - exhausted: a sparse/dead neighbourhood we can't fill to k live nodes — but if any
188
+ // reachable candidate were left, the loop above would have just queried it
189
+ // (which bumps in_flight), so in_flight == 0 also means "nothing left to
190
+ // try". (A stale query frees its slot, so it never hides a sendable one.)
191
+ // A fresh query always keeps in_flight > 0, so we never stop while a node might still
192
+ // answer inside its short timeout. Conversely a *stale* query — one past its short
193
+ // timeout, so almost certainly dead — is excluded from in_flight on purpose: that is what
194
+ // decouples termination from the 15 s full timeout. The full timeout now only ages out
195
+ // routing-table liveness (node_failed), never lookup completion, so one silent-but-close
196
+ // node can no longer stall the search (nor delay an announce_peer) by sitting out its
197
+ // full timeout.
198
+ return in_flight == 0;
199
+ }
200
+
201
+ void Traversal::on_responded(Observer& o, uint16_t rtt, TimePoint now) {
202
+ if (done_) return;
203
+ // The reply already set kAlive (see TraversalObserver::on_response); the rtt and
204
+ // alive flag let the routing table record a confirmed contact.
205
+ if (o.has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
206
+ --branch_factor_;
207
+ if (invoke_count_ > 0) --invoke_count_;
208
+
209
+ // A reply makes this a confirmed contact (its real id was resolved in on_response).
210
+ // BEP 42: prefer contacts whose id is derived from their IP.
211
+ table_.node_seen(o.id(), o.endpoint(), rtt, verify_node_id_for_ip(o.id(), o.endpoint().ip));
212
+
213
+ if (add_requests(now)) finish();
214
+ }
215
+
216
+ void Traversal::on_failed(Observer& o, bool short_timeout, TimePoint now) {
217
+ if (done_) return;
218
+ if (o.has(Observer::kDone) || o.has(Observer::kAlive) || o.has(Observer::kFailed)) return;
219
+
220
+ if (short_timeout) {
221
+ if (!o.has(Observer::kShortTimeout)) {
222
+ o.set(Observer::kShortTimeout);
223
+ ++branch_factor_; // free a slot but keep waiting for a possible late reply
224
+ }
225
+ if (add_requests(now)) finish();
226
+ return;
227
+ }
228
+
229
+ o.set(Observer::kFailed);
230
+ if (o.has(Observer::kShortTimeout) && branch_factor_ > static_cast<int>(kAlpha))
231
+ --branch_factor_;
232
+ if (invoke_count_ > 0) --invoke_count_;
233
+ if (!o.has(Observer::kInitial)) table_.node_failed(o.id(), o.endpoint());
234
+
235
+ if (add_requests(now)) finish();
236
+ }
237
+
238
+ void Traversal::traverse(const NodeId& id, const Address& ep) {
239
+ if (done_) return;
240
+ table_.heard_about(id, ep, verify_node_id_for_ip(id, ep.ip));
241
+ add_entry(id, ep);
242
+ }
243
+
244
+ void Traversal::resort_result(Observer& o) {
245
+ if (done_) return;
246
+
247
+ // Locate the observer by identity. It may have been dropped by a truncation while a
248
+ // reply was in flight, in which case there is nothing to re-place.
249
+ auto it = std::find_if(results_.begin(), results_.end(),
250
+ [&](const ObserverPtr& p) { return p.get() == &o; });
251
+ if (it == results_.end()) return;
252
+
253
+ // If it already sat inside the sorted prefix, account for pulling it out of there.
254
+ // (For a seed it lives in the unsorted tail, so this branch won't fire — but a
255
+ // duplicate id resolved into the prefix could, exactly as in libtorrent.)
256
+ if (it - results_.begin() < sorted_) --sorted_;
257
+
258
+ ObserverPtr ptr = std::move(*it);
259
+ results_.erase(it);
260
+
261
+ // Re-insert by XOR distance now that the id is known — same lower_bound the sorted
262
+ // prefix is built with in add_entry, so the invariant is preserved.
263
+ const NodeId id = ptr->id();
264
+ const auto sorted_end = results_.begin() + sorted_;
265
+ auto pos = std::lower_bound(results_.begin(), sorted_end, id,
266
+ [&](const ObserverPtr& a, const NodeId& nid) { return closer_to(a->id(), nid, target_); });
267
+ results_.insert(pos, std::move(ptr));
268
+ ++sorted_;
269
+ }
270
+
271
+ void Traversal::finish() {
272
+ if (done_) return;
273
+ done_ = true;
274
+
275
+ // Convergence mechanics, common to every lookup type — *how* it converged, which
276
+ // explains a slow or empty result (e.g. many queried but few alive = a sparse or
277
+ // unresponsive neighbourhood). The peer set and the elapsed time belong to the
278
+ // dht.find layer in node.cpp, so they are deliberately not repeated here.
279
+ int alive = 0, queried = 0;
280
+ for (const auto& o : results_) {
281
+ if (o->has(Observer::kAlive)) ++alive;
282
+ if (o->has(Observer::kQueried)) ++queried;
283
+ }
284
+ LOG_DEBUG("dht.find", name() << " L" << id_ << ' ' << short_hex(target_) << " converged in "
285
+ << round_ << " round(s): " << alive << " alive / " << queried
286
+ << " queried, " << results_.size() << " candidate(s)");
287
+
288
+ // We complete as soon as the top-k have answered, but queries to farther nodes may
289
+ // still be in flight (counted in invoke_count_, not in the top-k in_flight).
290
+ for (const auto& obs : results_) {
291
+ Observer* o = obs.get();
292
+ const bool in_flight = o->has(Observer::kQueried) && !o->has(Observer::kAlive) &&
293
+ !o->has(Observer::kFailed) && !o->has(Observer::kDone);
294
+ if (in_flight) {
295
+ o->set(Observer::kDone);
296
+ rpc_.cancel(o);
297
+ }
298
+ }
299
+
300
+ on_complete();
301
+ }
302
+
303
+ void TraversalObserver::on_response(const KrpcMessage& msg, uint16_t rtt_ms, TimePoint now) {
304
+ if (has(kDone) || has(kAlive)) return; // late/duplicate
305
+ if (algorithm_.finished()) return; // lookup already converged; don't re-enter parse_reply
306
+ set(kAlive); // protect against truncation during traverse()
307
+
308
+ if (has(kInitial) && msg.response_id != NodeId{}) {
309
+ // A seed/router we only knew by address just revealed its real id. Adopt it and
310
+ // re-place this observer among the distance-sorted candidates (a zero id is the
311
+ // "unknown" sentinel, so guard against a malformed reply re-sorting us to the top).
312
+ id_ = msg.response_id;
313
+ algorithm_.resort_result(*this);
314
+ }
315
+
316
+ parse_reply(msg);
317
+
318
+ // Adopt the nodes it pointed us at, then report the success (which may finish us).
319
+ for (const auto& kn : msg.nodes)
320
+ algorithm_.traverse(kn.id, Address(kn.ip, kn.port));
321
+
322
+ algorithm_.on_responded(*this, rtt_ms, now);
323
+ }
324
+
325
+ } // namespace dht
326
+ } // namespace librats
@@ -0,0 +1,120 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file traversal.h
5
+ * @brief Base of an iterative Kademlia lookup (libtorrent's traversal_algorithm).
6
+ *
7
+ * Holds the candidate set as a list of Observers kept sorted by XOR distance to the
8
+ * target, keeps up to branch_factor queries in flight, and converges on the k nodes
9
+ * closest to the target. Each reply feeds discovered nodes back in (and into the
10
+ * routing table); a two-level timeout keeps a slow node from stalling the search.
11
+ *
12
+ * Subclasses supply the three lookup-specific pieces: which query to send (invoke),
13
+ * how to read a reply (the observer subtype via make_observer), and what to do once
14
+ * it converges (on_complete).
15
+ */
16
+
17
+ #include "librats/dht/id.h"
18
+ #include "librats/dht/observer.h"
19
+ #include "librats/dht/routing_table.h"
20
+ #include "librats/dht/rpc_manager.h"
21
+
22
+ #include <cstddef>
23
+ #include <cstdint>
24
+ #include <unordered_set>
25
+ #include <vector>
26
+
27
+ namespace librats {
28
+ struct KrpcMessage;
29
+
30
+ namespace dht {
31
+
32
+ class Traversal {
33
+ public:
34
+ Traversal(RoutingTable& table, RpcManager& rpc, const NodeId& self, const NodeId& target);
35
+ virtual ~Traversal();
36
+
37
+ // Seed from the routing table and fire the first round of queries.
38
+ void start(TimePoint now);
39
+
40
+ const NodeId& target() const noexcept { return target_; }
41
+ bool finished() const noexcept { return done_; }
42
+ unsigned id() const noexcept { return id_; } // short per-lookup id for log correlation
43
+ virtual bool is_announce() const noexcept { return false; } // overridden by Announce
44
+
45
+ // Add a bootstrap/router contact whose node id we don't know yet (learned from
46
+ // its reply). Used to seed a lookup when the routing table is empty.
47
+ void add_seed(const Address& ep);
48
+
49
+ // -- driven by observers ----------------------------------------------------
50
+ void on_responded(Observer& o, uint16_t rtt, TimePoint now);
51
+ void on_failed(Observer& o, bool short_timeout, TimePoint now);
52
+ void traverse(const NodeId& id, const Address& ep); // a node a peer told us about
53
+
54
+ // A seed/router whose id we didn't know just told us its real id in a reply. Move it
55
+ // out of the unsorted tail and into its XOR-distance place among the sorted candidates
56
+ // (libtorrent's set_id → resort_result), so a seed close to the target can count
57
+ // toward the k closest and, for an announce, receive an announce_peer.
58
+ void resort_result(Observer& o);
59
+
60
+ protected:
61
+ // Build and send this node's query (returns false if it couldn't be sent).
62
+ virtual bool invoke(const ObserverPtr& o, TimePoint now) = 0;
63
+ virtual ObserverPtr make_observer(const NodeId& id, const Address& ep) = 0;
64
+ virtual void on_complete() {}
65
+ virtual const char* name() const { return "traversal"; }
66
+
67
+ void add_entry(const NodeId& id, const Address& ep, uint8_t flags = 0);
68
+
69
+ RoutingTable& table_;
70
+ RpcManager& rpc_;
71
+ NodeId self_;
72
+ // Candidate observers; the first `sorted_` are ordered by distance to target_.
73
+ std::vector<ObserverPtr> results_;
74
+
75
+ private:
76
+ bool add_requests(TimePoint now); // returns true when the search is complete
77
+ void finish();
78
+
79
+ // Anti-Sybil admission for a candidate's endpoint (libtorrent's dht_restrict_search_ips):
80
+ // record `ep`'s /24 (v4) or /64 (v6) in this lookup's prefix set and return true if that
81
+ // block was already claimed by an earlier candidate (⇒ ignore `ep`). Public IPs only;
82
+ // private/loopback/CGNAT never collide. O(1) amortised, replacing an O(n) scan.
83
+ bool register_subnet(const Address& ep);
84
+
85
+ NodeId target_;
86
+ unsigned id_; // unique lookup id (assigned in ctor), printed as "L<id>" in logs
87
+ int round_ = 0; // how many waves of queries we've fired (the search "round")
88
+ int sorted_ = 0;
89
+ int branch_factor_ = static_cast<int>(kAlpha);
90
+ int invoke_count_ = 0;
91
+ bool done_ = false;
92
+
93
+ // Packed /24 (v4) and /64 (v6) prefixes of candidates already admitted to this lookup,
94
+ // backing register_subnet()'s O(1) Sybil check. Bounded by kMaxResults, so tiny.
95
+ std::unordered_set<uint32_t> subnet4_;
96
+ std::unordered_set<uint64_t> subnet6_;
97
+
98
+ static constexpr std::size_t kMaxResults = 100;
99
+ };
100
+
101
+ // Common observer for nodes inside a traversal: forwards timeouts to the algorithm,
102
+ // and on a reply learns the responder's id, lets the subclass read lookup-specific
103
+ // fields (parse_reply), folds in the nodes it returned, then reports back.
104
+ class TraversalObserver : public Observer {
105
+ public:
106
+ TraversalObserver(Traversal& algorithm, const NodeId& id, const Address& ep)
107
+ : Observer(id, ep), algorithm_(algorithm) {}
108
+
109
+ void on_response(const KrpcMessage& msg, uint16_t rtt_ms, TimePoint now) override;
110
+ void on_timeout(TimePoint now) override { algorithm_.on_failed(*this, /*short=*/false, now); }
111
+ void on_short_timeout(TimePoint now) override { algorithm_.on_failed(*this, /*short=*/true, now); }
112
+
113
+ protected:
114
+ virtual void parse_reply(const KrpcMessage& msg) {} // subclass collects peers/token/etc.
115
+
116
+ Traversal& algorithm_;
117
+ };
118
+
119
+ } // namespace dht
120
+ } // namespace librats
@@ -0,0 +1,49 @@
1
+ #include "librats/dht/udp_transport.h"
2
+
3
+ namespace librats {
4
+ namespace dht {
5
+
6
+ UdpTransport::UdpTransport(int port, const std::string& bind_address, AddressFamily family)
7
+ : family_(family) {
8
+ init_socket_library(); // idempotent
9
+
10
+ socket_ = create_udp_socket(port, bind_address, family_);
11
+ if (!is_valid_socket(socket_) && port > 0) {
12
+ // A specific port was requested but unavailable — fall back to an ephemeral one.
13
+ // (A negative/invalid port is a hard error, not a fallback case.)
14
+ socket_ = create_udp_socket(0, bind_address, family_);
15
+ }
16
+ if (is_valid_socket(socket_)) {
17
+ port_ = static_cast<uint16_t>(get_bound_port(socket_));
18
+ set_socket_nonblocking(socket_); // recv() drives readiness via its timeout
19
+ }
20
+ }
21
+
22
+ UdpTransport::~UdpTransport() {
23
+ close();
24
+ }
25
+
26
+ void UdpTransport::send(const Address& to, const std::vector<uint8_t>& datagram) {
27
+ if (!is_open()) return;
28
+ send_udp_data(socket_, datagram, to, family_);
29
+ }
30
+
31
+ std::optional<std::vector<uint8_t>> UdpTransport::recv(int timeout_ms, Address& from,
32
+ socket_t interrupt_fd) {
33
+ if (!is_open()) return std::nullopt;
34
+ // 4 KiB comfortably covers any KRPC datagram; an oversized one would be dropped
35
+ // (truncated to WSAEMSGSIZE on Windows), so leave headroom above the ~1500 MTU.
36
+ auto data = receive_udp_data(socket_, 4096, from, timeout_ms, interrupt_fd);
37
+ if (data.empty()) return std::nullopt;
38
+ return data;
39
+ }
40
+
41
+ void UdpTransport::close() {
42
+ if (is_valid_socket(socket_)) {
43
+ close_socket(socket_);
44
+ socket_ = RATS_INVALID_SOCKET;
45
+ }
46
+ }
47
+
48
+ } // namespace dht
49
+ } // namespace librats
@@ -0,0 +1,51 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file udp_transport.h
5
+ * @brief The real UDP socket behind the DHT's Transport interface.
6
+ *
7
+ * Owns one single-family UDP socket. send() is what dht::Node uses to put datagrams
8
+ * on the wire; recv() is the blocking-with-timeout read the DhtRunner loop pumps.
9
+ * Pure I/O — no DHT logic lives here.
10
+ */
11
+
12
+ #include "librats/core/socket.h"
13
+ #include "librats/dht/transport.h"
14
+
15
+ #include <cstdint>
16
+ #include <optional>
17
+ #include <string>
18
+ #include <vector>
19
+
20
+ namespace librats {
21
+ namespace dht {
22
+
23
+ class UdpTransport : public Transport {
24
+ public:
25
+ // Binds a UDP socket on `port` (0 = ephemeral) for `family`. If the requested
26
+ // port is taken it falls back to an ephemeral one. Check is_open() after.
27
+ UdpTransport(int port, const std::string& bind_address, AddressFamily family);
28
+ ~UdpTransport() override;
29
+
30
+ bool is_open() const noexcept { return is_valid_socket(socket_); }
31
+ uint16_t port() const noexcept { return port_; } // actual bound port
32
+
33
+ void send(const Address& to, const std::vector<uint8_t>& datagram) override;
34
+
35
+ // Wait up to timeout_ms for a datagram. Returns the payload and fills `from` on
36
+ // success, or nullopt on timeout / error. If `interrupt_fd` is a valid socket it is
37
+ // watched alongside the data socket: when it becomes readable the wait returns early
38
+ // (as nullopt), letting the caller react to posted work without waiting out the timeout.
39
+ std::optional<std::vector<uint8_t>> recv(int timeout_ms, Address& from,
40
+ socket_t interrupt_fd = RATS_INVALID_SOCKET);
41
+
42
+ void close();
43
+
44
+ private:
45
+ socket_t socket_ = RATS_INVALID_SOCKET;
46
+ AddressFamily family_;
47
+ uint16_t port_ = 0;
48
+ };
49
+
50
+ } // namespace dht
51
+ } // namespace librats
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file log.h
5
+ * @brief Small logging helpers for the mDNS module.
6
+ *
7
+ * Formatting sugar on top of util/logger.h — nothing more. mDNS logs under the "mdns"
8
+ * tag so it gets its own stable colour and is easy to grep.
9
+ *
10
+ * This lives apart from mdns.h on purpose. mdns.h is reachable from a public header
11
+ * (subsystems/mdns_discovery.h), and util/logger.h defines unprefixed macros —
12
+ * LOG_DEBUG/LOG_INFO/LOG_WARN/LOG_ERROR, plus isatty/fileno on Windows — that would
13
+ * then land in every consumer translation unit. Keep the logging include on the .cpp
14
+ * side, exactly as src/dht/log.h and src/bittorrent/log.h do.
15
+ */
16
+
17
+ #include "librats/util/logger.h"
18
+
19
+ #define LOG_MDNS_DEBUG(message) LOG_DEBUG("mdns", message)
20
+ #define LOG_MDNS_INFO(message) LOG_INFO("mdns", message)
21
+ #define LOG_MDNS_WARN(message) LOG_WARN("mdns", message)
22
+ #define LOG_MDNS_ERROR(message) LOG_ERROR("mdns", message)