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,374 @@
1
+ #include "librats/bittorrent/tracker.h"
2
+
3
+ #include "librats/bittorrent/bencode.h"
4
+ #include "librats/bittorrent/byte_io.h"
5
+ #include "librats/bittorrent/log.h"
6
+ #include "librats/core/socket.h"
7
+ #include "librats/util/network_utils.h"
8
+
9
+ #include <algorithm>
10
+ #include <cctype>
11
+ #include <cstring>
12
+ #include <iomanip>
13
+ #include <random>
14
+ #include <sstream>
15
+ #include <thread>
16
+
17
+ namespace librats::bittorrent {
18
+
19
+ namespace {
20
+
21
+ // UDP BEP 15 constants.
22
+ constexpr std::int64_t kUdpMagic = 0x41727101980LL;
23
+ constexpr std::uint32_t kActionConnect = 0;
24
+ constexpr std::uint32_t kActionAnnounce = 1;
25
+ constexpr std::uint32_t kActionError = 3;
26
+
27
+ std::uint32_t random_u32() {
28
+ thread_local std::mt19937 gen(std::random_device{}());
29
+ return std::uniform_int_distribution<std::uint32_t>{}(gen);
30
+ }
31
+
32
+ // Parse a TCP/UDP port out of an untrusted tracker URL. Returns 0 on anything
33
+ // that is not a plain 1..65535 decimal number. std::stoi must NOT be used here:
34
+ // a hostile ".torrent"/magnet `tr=udp://host:notaport` would throw
35
+ // std::invalid_argument out of the detached announce worker → std::terminate.
36
+ std::uint16_t parse_port(const std::string& s) {
37
+ if (s.empty()) return 0;
38
+ std::uint32_t v = 0;
39
+ for (const char c : s) {
40
+ if (c < '0' || c > '9') return 0;
41
+ v = v * 10 + std::uint32_t(c - '0');
42
+ if (v > 65535) return 0;
43
+ }
44
+ return std::uint16_t(v);
45
+ }
46
+
47
+ const char* http_event(TrackerEvent e) {
48
+ switch (e) {
49
+ case TrackerEvent::Started: return "started";
50
+ case TrackerEvent::Stopped: return "stopped";
51
+ case TrackerEvent::Completed: return "completed";
52
+ default: return "";
53
+ }
54
+ }
55
+
56
+ // UDP event codes differ from our enum order, so map explicitly.
57
+ std::uint32_t udp_event(TrackerEvent e) {
58
+ switch (e) {
59
+ case TrackerEvent::Completed: return 1;
60
+ case TrackerEvent::Started: return 2;
61
+ case TrackerEvent::Stopped: return 3;
62
+ default: return 0;
63
+ }
64
+ }
65
+
66
+ std::string url_encode_binary(const std::uint8_t* data, std::size_t len) {
67
+ std::ostringstream o;
68
+ o.fill('0');
69
+ o << std::hex;
70
+ for (std::size_t i = 0; i < len; ++i) {
71
+ const std::uint8_t c = data[i];
72
+ if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') o << char(c);
73
+ else o << '%' << std::setw(2) << int(c);
74
+ }
75
+ return o.str();
76
+ }
77
+
78
+ std::vector<Address> parse_compact_peers(const std::string& data) {
79
+ std::vector<Address> peers;
80
+ for (std::size_t i = 0; i + 6 <= data.size(); i += 6) {
81
+ const auto* b = reinterpret_cast<const std::uint8_t*>(data.data() + i);
82
+ std::ostringstream ip;
83
+ ip << int(b[0]) << '.' << int(b[1]) << '.' << int(b[2]) << '.' << int(b[3]);
84
+ peers.emplace_back(ip.str(), read_u16_be(b + 4));
85
+ }
86
+ return peers;
87
+ }
88
+
89
+ std::vector<Address> parse_dict_peers(const librats::BencodeValue& list) {
90
+ std::vector<Address> peers;
91
+ for (std::size_t i = 0; i < list.size(); ++i) {
92
+ const auto& d = list[i];
93
+ if (!d.is_dict() || !d.has_key("ip") || !d.has_key("port")) continue;
94
+ const std::uint16_t port = std::uint16_t(d["port"].as_integer());
95
+ if (port == 0) continue;
96
+ // BEP 3 dictionary model: "ip" may be a dotted-quad, an IPv6 literal, OR a
97
+ // DNS name. Address is strictly numeric, so a name must be resolved here
98
+ // (we already run on a blocking announce worker) — feeding it to the numeric
99
+ // Address ctor would assert in debug and silently drop the peer in release.
100
+ const std::string& host = d["ip"].as_string();
101
+ auto ip = IpAddress::parse(host);
102
+ if (!ip) {
103
+ std::string resolved = network_utils::resolve_hostname(host);
104
+ if (resolved.empty()) resolved = network_utils::resolve_hostname_v6(host);
105
+ ip = IpAddress::parse(resolved);
106
+ }
107
+ if (ip) peers.emplace_back(*ip, port);
108
+ }
109
+ return peers;
110
+ }
111
+
112
+ /// Blocking HTTP/1.0 GET; returns the response body (headers stripped).
113
+ Bytes http_get(const std::string& url, int timeout_ms) {
114
+ const std::size_t scheme = url.find("://");
115
+ if (scheme == std::string::npos) return {};
116
+ const std::string proto = url.substr(0, scheme);
117
+ const std::size_t host_start = scheme + 3;
118
+ const std::size_t path_start = url.find('/', host_start);
119
+
120
+ std::string host_port = url.substr(host_start, path_start == std::string::npos ? std::string::npos
121
+ : path_start - host_start);
122
+ std::string path = (path_start == std::string::npos) ? "/" : url.substr(path_start);
123
+
124
+ std::uint16_t port = (proto == "https") ? 443 : 80;
125
+ if (const std::size_t colon = host_port.find(':'); colon != std::string::npos) {
126
+ const std::uint16_t parsed = parse_port(host_port.substr(colon + 1));
127
+ if (parsed == 0) return {}; // malformed port — fail the announce, never crash
128
+ port = parsed;
129
+ host_port = host_port.substr(0, colon);
130
+ }
131
+
132
+ socket_t sock = create_tcp_client(host_port, port, timeout_ms);
133
+ if (!is_valid_socket(sock)) return {};
134
+
135
+ std::ostringstream req;
136
+ req << "GET " << path << " HTTP/1.0\r\nHost: " << host_port
137
+ << "\r\nUser-Agent: librats\r\nAccept: */*\r\nConnection: close\r\n\r\n";
138
+ if (send_tcp_string(sock, req.str()) <= 0) { close_socket(sock); return {}; }
139
+
140
+ Bytes data;
141
+ for (;;) {
142
+ Bytes chunk = receive_tcp_data(sock, 4096);
143
+ if (chunk.empty()) break;
144
+ data.insert(data.end(), chunk.begin(), chunk.end());
145
+ }
146
+ close_socket(sock);
147
+
148
+ // Strip headers (everything up to and including the blank line).
149
+ static const std::uint8_t sep[4] = {'\r', '\n', '\r', '\n'};
150
+ for (std::size_t i = 0; i + 4 <= data.size(); ++i) {
151
+ if (std::memcmp(data.data() + i, sep, 4) == 0)
152
+ return Bytes(data.begin() + std::ptrdiff_t(i + 4), data.end());
153
+ }
154
+ return {};
155
+ }
156
+
157
+ // Wait for a UDP datagram for up to @p total_timeout_ms, but in short slices so a
158
+ // pending cancellation (e.g. shutdown) aborts the wait within one slice instead
159
+ // of blocking for the whole timeout against an unresponsive tracker. Returns the
160
+ // datagram, or empty on timeout / cancellation.
161
+ Bytes receive_udp_sliced(socket_t sock, std::size_t maxlen, Address& sender, int total_timeout_ms,
162
+ const TrackerCancelPredicate& cancelled) {
163
+ if (!cancelled) {
164
+ // No cancellation wanted — one plain blocking wait (fast path).
165
+ return receive_udp_data(sock, maxlen, sender, total_timeout_ms);
166
+ }
167
+ constexpr int kSliceMs = 200;
168
+ int elapsed = 0;
169
+ while (elapsed < total_timeout_ms) {
170
+ if (cancelled()) return {};
171
+ const int slice = (std::min)(kSliceMs, total_timeout_ms - elapsed);
172
+ Bytes data = receive_udp_data(sock, maxlen, sender, slice);
173
+ if (!data.empty()) return data;
174
+ elapsed += slice;
175
+ }
176
+ return {};
177
+ }
178
+
179
+ TrackerResponse announce_http(const std::string& url, const TrackerRequest& req, int timeout_ms,
180
+ const TrackerCancelPredicate& cancelled) {
181
+ TrackerResponse out;
182
+ if (cancelled && cancelled()) { out.failure_reason = "cancelled"; return out; }
183
+ const Bytes body = http_get(tracker_detail::build_http_announce_url(url, req), timeout_ms);
184
+ if (body.empty()) { out.failure_reason = "no response"; return out; }
185
+ return tracker_detail::parse_http_response(body);
186
+ }
187
+
188
+ TrackerResponse announce_udp(const std::string& url, const TrackerRequest& req, int timeout_ms,
189
+ const TrackerCancelPredicate& cancelled) {
190
+ TrackerResponse out;
191
+ if (cancelled && cancelled()) { out.failure_reason = "cancelled"; return out; }
192
+
193
+ // Parse udp://host:port[/...]
194
+ std::string rest = url.substr(6);
195
+ if (const std::size_t slash = rest.find('/'); slash != std::string::npos) rest = rest.substr(0, slash);
196
+ const std::size_t colon = rest.find(':');
197
+ if (colon == std::string::npos) { out.failure_reason = "bad udp url"; return out; }
198
+ const std::string host = rest.substr(0, colon);
199
+ const std::uint16_t port = parse_port(rest.substr(colon + 1));
200
+ if (port == 0) { out.failure_reason = "bad udp port"; return out; }
201
+
202
+ socket_t sock = create_udp_socket(0, "", AddressFamily::IPv4);
203
+ if (!is_valid_socket(sock)) { out.failure_reason = "socket"; return out; }
204
+ struct Closer { socket_t s; ~Closer() { if (is_valid_socket(s)) close_socket(s); } } closer{sock};
205
+
206
+ // 1) connect
207
+ const std::uint32_t tid1 = random_u32();
208
+ Bytes creq(16);
209
+ write_u64_be(creq.data(), std::uint64_t(kUdpMagic));
210
+ write_u32_be(creq.data() + 8, kActionConnect);
211
+ write_u32_be(creq.data() + 12, tid1);
212
+ if (send_udp_data(sock, creq, host, port, AddressFamily::IPv4) <= 0) { out.failure_reason = "send"; return out; }
213
+
214
+ Address sender;
215
+ Bytes cresp = receive_udp_sliced(sock, 2048, sender, timeout_ms, cancelled);
216
+ if (cresp.size() < 16 || read_u32_be(cresp.data()) != kActionConnect ||
217
+ read_u32_be(cresp.data() + 4) != tid1) {
218
+ out.failure_reason = (cancelled && cancelled()) ? "cancelled" : "connect failed";
219
+ return out;
220
+ }
221
+ const std::uint64_t conn_id = read_u64_be(cresp.data() + 8);
222
+
223
+ // 2) announce
224
+ const std::uint32_t tid2 = random_u32();
225
+ Bytes areq(98);
226
+ write_u64_be(areq.data(), conn_id);
227
+ write_u32_be(areq.data() + 8, kActionAnnounce);
228
+ write_u32_be(areq.data() + 12, tid2);
229
+ std::memcpy(areq.data() + 16, req.info_hash.data(), 20);
230
+ std::memcpy(areq.data() + 36, req.peer_id.data(), 20);
231
+ write_u64_be(areq.data() + 56, req.downloaded);
232
+ write_u64_be(areq.data() + 64, req.left);
233
+ write_u64_be(areq.data() + 72, req.uploaded);
234
+ write_u32_be(areq.data() + 80, udp_event(req.event));
235
+ write_u32_be(areq.data() + 84, 0); // our IP — let the tracker use the source
236
+ write_u32_be(areq.data() + 88, random_u32()); // key
237
+ write_u32_be(areq.data() + 92, std::uint32_t(req.numwant));
238
+ write_u16_be(areq.data() + 96, req.port);
239
+ if (send_udp_data(sock, areq, host, port, AddressFamily::IPv4) <= 0) { out.failure_reason = "send"; return out; }
240
+
241
+ Bytes resp = receive_udp_sliced(sock, 2048, sender, timeout_ms, cancelled);
242
+ if (resp.size() < 8) {
243
+ out.failure_reason = (cancelled && cancelled()) ? "cancelled" : "no response";
244
+ return out;
245
+ }
246
+ const std::uint32_t action = read_u32_be(resp.data());
247
+ if (action == kActionError) {
248
+ out.failure_reason.assign(resp.begin() + 8, resp.end());
249
+ return out;
250
+ }
251
+ if (action != kActionAnnounce || read_u32_be(resp.data() + 4) != tid2 || resp.size() < 20) {
252
+ out.failure_reason = "bad announce response";
253
+ return out;
254
+ }
255
+
256
+ out.interval = read_u32_be(resp.data() + 8);
257
+ out.incomplete = read_u32_be(resp.data() + 12);
258
+ out.complete = read_u32_be(resp.data() + 16);
259
+ for (std::size_t off = 20; off + 6 <= resp.size(); off += 6) {
260
+ const std::uint8_t* b = resp.data() + off;
261
+ std::ostringstream ip;
262
+ ip << int(b[0]) << '.' << int(b[1]) << '.' << int(b[2]) << '.' << int(b[3]);
263
+ out.peers.emplace_back(ip.str(), read_u16_be(b + 4));
264
+ }
265
+ out.success = true;
266
+ return out;
267
+ }
268
+
269
+ } // namespace
270
+
271
+ namespace tracker_detail {
272
+
273
+ std::string build_http_announce_url(const std::string& base, const TrackerRequest& req) {
274
+ std::ostringstream u;
275
+ u << base << (base.find('?') == std::string::npos ? '?' : '&');
276
+ u << "info_hash=" << url_encode_binary(req.info_hash.data(), req.info_hash.size());
277
+ u << "&peer_id=" << url_encode_binary(req.peer_id.data(), req.peer_id.size());
278
+ u << "&port=" << req.port;
279
+ u << "&uploaded=" << req.uploaded;
280
+ u << "&downloaded=" << req.downloaded;
281
+ u << "&left=" << req.left;
282
+ u << "&numwant=" << req.numwant;
283
+ u << "&compact=1";
284
+ if (req.event != TrackerEvent::None) u << "&event=" << http_event(req.event);
285
+ return u.str();
286
+ }
287
+
288
+ TrackerResponse parse_http_response(const Bytes& body) {
289
+ TrackerResponse out;
290
+ try {
291
+ librats::BencodeValue d = librats::BencodeDecoder::decode(body.data(), body.size());
292
+ if (!d.is_dict()) { out.failure_reason = "not a dict"; return out; }
293
+ if (d.has_key("failure reason")) { out.failure_reason = d["failure reason"].as_string(); return out; }
294
+ if (d.has_key("interval")) out.interval = std::uint32_t(d["interval"].as_integer());
295
+ if (d.has_key("min interval")) out.min_interval = std::uint32_t(d["min interval"].as_integer());
296
+ if (d.has_key("complete")) out.complete = std::uint32_t(d["complete"].as_integer());
297
+ if (d.has_key("incomplete")) out.incomplete = std::uint32_t(d["incomplete"].as_integer());
298
+ if (d.has_key("peers")) {
299
+ const auto& p = d["peers"];
300
+ if (p.is_string()) out.peers = parse_compact_peers(p.as_string());
301
+ else if (p.is_list()) out.peers = parse_dict_peers(p);
302
+ }
303
+ out.success = true;
304
+ } catch (const std::exception& e) {
305
+ out.failure_reason = e.what();
306
+ }
307
+ return out;
308
+ }
309
+
310
+ } // namespace tracker_detail
311
+
312
+ TrackerResponse announce_to_tracker(const std::string& url, const TrackerRequest& req, int timeout_ms,
313
+ const TrackerCancelPredicate& cancelled) {
314
+ if (url.rfind("udp://", 0) == 0) return announce_udp(url, req, timeout_ms, cancelled);
315
+ // We have no TLS for trackers yet. Skip https:// rather than speaking cleartext
316
+ // HTTP to port 443 — that never works and would leak info_hash/peer_id in the
317
+ // clear (H11). Re-enable once a real TLS stream is wired in.
318
+ if (url.rfind("https://", 0) == 0) {
319
+ TrackerResponse out; out.failure_reason = "https trackers not supported (no TLS)";
320
+ return out;
321
+ }
322
+ if (url.rfind("http://", 0) == 0) return announce_http(url, req, timeout_ms, cancelled);
323
+ TrackerResponse out;
324
+ out.failure_reason = "unsupported tracker scheme";
325
+ return out;
326
+ }
327
+
328
+ // ---- TrackerAnnouncer ----
329
+
330
+ TrackerAnnouncer::TrackerAnnouncer(std::vector<std::string> trackers, Poster poster, int timeout_ms)
331
+ : trackers_(std::move(trackers)), poster_(std::move(poster)), timeout_ms_(timeout_ms) {}
332
+
333
+ TrackerAnnouncer::~TrackerAnnouncer() { stop(); }
334
+
335
+ void TrackerAnnouncer::announce(const TrackerRequest& req, PeerCallback on_peers) {
336
+ for (const std::string& url : trackers_) {
337
+ {
338
+ std::lock_guard<std::mutex> lk(mutex_);
339
+ if (stopping_) return;
340
+ ++inflight_;
341
+ }
342
+ std::thread([this, url, req, on_peers] {
343
+ TrackerResponse resp = announce_to_tracker(url, req, timeout_ms_);
344
+ if (resp.success)
345
+ LOG_DEBUG("bt.tracker", "← " << url << ": " << resp.peers.size() << " peers, interval "
346
+ << resp.interval << "s");
347
+ else
348
+ LOG_DEBUG("bt.tracker", "✗ " << url << ": " << resp.failure_reason);
349
+ {
350
+ std::lock_guard<std::mutex> lk(mutex_);
351
+ if (!stopping_ && resp.success && poster_) {
352
+ // Honour the tracker's requested re-announce cadence (clamped to a
353
+ // sane 1 min..1 h) so we don't get rate-limited/banned (H13).
354
+ std::uint32_t interval = (std::max)(resp.interval, resp.min_interval);
355
+ if (interval == 0) interval = 1800;
356
+ interval = std::min<std::uint32_t>(std::max<std::uint32_t>(interval, 60), 3600);
357
+ poster_([on_peers, peers = resp.peers, interval] { on_peers(peers, interval); });
358
+ }
359
+ --inflight_;
360
+ // Notify while still holding the lock: once inflight_ hits zero a waiting
361
+ // stop() may return and the announcer be destroyed, taking drain_cv_ with it.
362
+ drain_cv_.notify_all();
363
+ }
364
+ }).detach();
365
+ }
366
+ }
367
+
368
+ void TrackerAnnouncer::stop() {
369
+ std::unique_lock<std::mutex> lk(mutex_);
370
+ stopping_ = true;
371
+ drain_cv_.wait(lk, [this] { return inflight_ == 0; });
372
+ }
373
+
374
+ } // namespace librats::bittorrent
@@ -0,0 +1,108 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file tracker.h
5
+ * @brief Tracker announces (BEP 3 over HTTP, BEP 15 over UDP) and the per-torrent
6
+ * announcer that drives them.
7
+ *
8
+ * A tracker is a directory: you periodically POST your progress and it hands back
9
+ * a list of peers. The wire work is blocking (a short HTTP request or a two-step
10
+ * UDP connect→announce), so announce_to_tracker() is a self-contained blocking
11
+ * call with no shared state — easy to test and safe to run off-thread.
12
+ *
13
+ * TrackerAnnouncer is what a Torrent owns: it fires announces to every tracker on
14
+ * background threads and delivers discovered peers back through a poster (onto the
15
+ * reactor), exactly like the disk subsystem. stop() joins any in-flight announce
16
+ * so a torrent never tears down underneath one.
17
+ */
18
+
19
+ #include "librats/bittorrent/types.h"
20
+ #include "librats/core/address.h"
21
+ #include "librats/core/bytes.h"
22
+
23
+ #include <condition_variable>
24
+ #include <cstdint>
25
+ #include <functional>
26
+ #include <mutex>
27
+ #include <string>
28
+ #include <vector>
29
+
30
+ namespace librats::bittorrent {
31
+
32
+ enum class TrackerEvent { None, Started, Stopped, Completed };
33
+
34
+ struct TrackerRequest {
35
+ InfoHash info_hash{};
36
+ PeerId peer_id{};
37
+ std::uint16_t port = 0;
38
+ std::uint64_t uploaded = 0;
39
+ std::uint64_t downloaded = 0;
40
+ std::uint64_t left = 0;
41
+ TrackerEvent event = TrackerEvent::None;
42
+ int numwant = 50;
43
+ };
44
+
45
+ struct TrackerResponse {
46
+ bool success = false;
47
+ std::string failure_reason;
48
+ std::uint32_t interval = 1800; ///< seconds until the next announce
49
+ std::uint32_t min_interval = 0;
50
+ std::uint32_t complete = 0; ///< seeders
51
+ std::uint32_t incomplete = 0; ///< leechers
52
+ std::vector<Address> peers;
53
+ };
54
+
55
+ /// Polled by the blocking announce so a caller can abort it early (e.g. on
56
+ /// shutdown): when it returns true the announce bails within ~one poll slice
57
+ /// instead of blocking for the full timeout. An empty predicate never cancels.
58
+ using TrackerCancelPredicate = std::function<bool()>;
59
+
60
+ /// Perform one blocking announce to @p url (http/https/udp). Self-contained: it
61
+ /// opens, uses and closes its own socket and shares no state with anything.
62
+ /// @p cancelled, if set, is polled during the wait so the call can be aborted
63
+ /// promptly (the blocking receive is done in short slices between polls).
64
+ TrackerResponse announce_to_tracker(const std::string& url, const TrackerRequest& req,
65
+ int timeout_ms = 10000,
66
+ const TrackerCancelPredicate& cancelled = {});
67
+
68
+ /// Drives announces to a torrent's trackers from background threads, delivering
69
+ /// peers via a poster (the reactor). Owned by one Torrent.
70
+ class TrackerAnnouncer {
71
+ public:
72
+ using Poster = std::function<void(std::function<void()>)>;
73
+ /// Delivers a tracker's response: discovered peers and the interval (seconds)
74
+ /// the tracker wants before the next announce (already max'd with min-interval).
75
+ using PeerCallback = std::function<void(const std::vector<Address>& peers, std::uint32_t interval)>;
76
+
77
+ TrackerAnnouncer(std::vector<std::string> trackers, Poster poster, int timeout_ms = 10000);
78
+ ~TrackerAnnouncer();
79
+
80
+ TrackerAnnouncer(const TrackerAnnouncer&) = delete;
81
+ TrackerAnnouncer& operator=(const TrackerAnnouncer&) = delete;
82
+
83
+ /// Announce to every tracker (each on its own worker thread); @p on_peers is
84
+ /// posted onto the reactor for each tracker that returns peers.
85
+ void announce(const TrackerRequest& req, PeerCallback on_peers);
86
+ /// Wait for any in-flight announce to finish. Idempotent; called by the dtor.
87
+ void stop();
88
+
89
+ std::size_t tracker_count() const noexcept { return trackers_.size(); }
90
+
91
+ private:
92
+ std::vector<std::string> trackers_;
93
+ Poster poster_;
94
+ int timeout_ms_;
95
+
96
+ std::mutex mutex_;
97
+ std::condition_variable drain_cv_;
98
+ int inflight_ = 0;
99
+ bool stopping_ = false;
100
+ };
101
+
102
+ // Exposed for unit testing the HTTP wire format without a live tracker.
103
+ namespace tracker_detail {
104
+ std::string build_http_announce_url(const std::string& base, const TrackerRequest& req);
105
+ TrackerResponse parse_http_response(const Bytes& body);
106
+ } // namespace tracker_detail
107
+
108
+ } // namespace librats::bittorrent