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,314 @@
1
+ #include "librats/bittorrent/torrent_info.h"
2
+
3
+ #include "librats/bittorrent/bencode.h"
4
+ #include "librats/bittorrent/magnet_uri.h"
5
+ #include "librats/crypto/sha1.h"
6
+ #include "librats/util/fs.h"
7
+
8
+ #include <cctype>
9
+ #include <cstddef>
10
+ #include <cstring>
11
+
12
+ namespace librats::bittorrent {
13
+
14
+ namespace {
15
+
16
+ // ----------------------------------------------------------------------------
17
+ // Minimal bencode byte-span scanner.
18
+ //
19
+ // We must hash the *original* bytes of the `info` value (re-encoding the parsed
20
+ // structure could reorder dict keys and change the hash). The eager BencodeValue
21
+ // decoder discards positions, so this scanner walks the raw buffer to locate the
22
+ // info value's byte range without building a tree.
23
+ // ----------------------------------------------------------------------------
24
+
25
+ /// Read a bencoded byte string at @p pos, advancing @p pos past it.
26
+ bool read_bstring(const std::uint8_t* d, std::size_t n, std::size_t& pos, std::string& out) {
27
+ std::size_t colon = pos;
28
+ while (colon < n && d[colon] != ':') {
29
+ if (d[colon] < '0' || d[colon] > '9') return false;
30
+ ++colon;
31
+ }
32
+ if (colon >= n || colon == pos) return false;
33
+ std::uint64_t len = 0;
34
+ for (std::size_t i = pos; i < colon; ++i) {
35
+ len = len * 10 + std::uint64_t(d[i] - '0');
36
+ if (len > n) return false; // can't be longer than the whole buffer
37
+ }
38
+ const std::size_t start = colon + 1;
39
+ if (start + len > n) return false;
40
+ out.assign(reinterpret_cast<const char*>(d + start), len);
41
+ pos = start + len;
42
+ return true;
43
+ }
44
+
45
+ /// Advance @p pos past exactly one bencoded value (int / string / list / dict).
46
+ bool skip_value(const std::uint8_t* d, std::size_t n, std::size_t& pos) {
47
+ if (pos >= n) return false;
48
+ const char c = char(d[pos]);
49
+ if (c == 'i') {
50
+ std::size_t e = pos + 1;
51
+ while (e < n && d[e] != 'e') ++e;
52
+ if (e >= n) return false;
53
+ pos = e + 1;
54
+ return true;
55
+ }
56
+ if (c == 'l' || c == 'd') {
57
+ ++pos;
58
+ while (pos < n && d[pos] != 'e') {
59
+ if (!skip_value(d, n, pos)) return false;
60
+ }
61
+ if (pos >= n) return false;
62
+ ++pos; // consume 'e'
63
+ return true;
64
+ }
65
+ if (c >= '0' && c <= '9') {
66
+ std::string tmp;
67
+ return read_bstring(d, n, pos, tmp);
68
+ }
69
+ return false;
70
+ }
71
+
72
+ /// Locate the byte range [start, end) of the top-level dict's `info` value.
73
+ std::optional<std::pair<std::size_t, std::size_t>> find_info_span(const Bytes& buf) {
74
+ const std::uint8_t* d = buf.data();
75
+ const std::size_t n = buf.size();
76
+ if (n == 0 || d[0] != 'd') return std::nullopt;
77
+
78
+ std::size_t pos = 1;
79
+ while (pos < n && d[pos] != 'e') {
80
+ std::string key;
81
+ if (!read_bstring(d, n, pos, key)) return std::nullopt;
82
+ const std::size_t value_start = pos;
83
+ if (!skip_value(d, n, pos)) return std::nullopt;
84
+ if (key == "info") return std::make_pair(value_start, pos);
85
+ }
86
+ return std::nullopt;
87
+ }
88
+
89
+ // ---- safe bencode accessors (the BencodeValue accessors throw on mismatch) ----
90
+
91
+ const librats::BencodeValue* find(const librats::BencodeValue& dict, const char* key) {
92
+ return dict.find(key);
93
+ }
94
+
95
+ const std::string* find_string(const librats::BencodeValue& dict, const char* key) {
96
+ const auto* v = find(dict, key);
97
+ return (v && v->is_string()) ? &v->as_string() : nullptr;
98
+ }
99
+
100
+ std::optional<std::int64_t> find_int(const librats::BencodeValue& dict, const char* key) {
101
+ const auto* v = find(dict, key);
102
+ if (v && v->is_integer()) return v->as_integer();
103
+ return std::nullopt;
104
+ }
105
+
106
+ /// A path component is safe if it is non-empty, not "." / ".." and free of
107
+ /// separators / NULs — guarding the disk layer against traversal.
108
+ bool safe_component(const std::string& c) {
109
+ if (c.empty() || c == "." || c == "..") return false;
110
+ return c.find('/') == std::string::npos && c.find('\\') == std::string::npos
111
+ && c.find('\0') == std::string::npos;
112
+ }
113
+
114
+ } // namespace
115
+
116
+ bool TorrentInfo::parse_info_dict(const librats::BencodeValue& info, TorrentParseError* err) {
117
+ auto fail = [&](const char* m) { if (err) err->message = m; return false; };
118
+
119
+ const std::string* name = find_string(info, "name");
120
+ const auto piece_length = find_int(info, "piece length");
121
+ const std::string* pieces = find_string(info, "pieces");
122
+ if (!name || !piece_length || !pieces) return fail("info dict missing name/piece length/pieces");
123
+ if (*piece_length <= 0 || *piece_length > (1 << 30)) return fail("invalid piece length");
124
+ if (pieces->size() % kInfoHashSize != 0) return fail("pieces string not a multiple of 20");
125
+ if (!safe_component(*name)) return fail("unsafe torrent name");
126
+
127
+ FileStorage files;
128
+ files.set_piece_length(std::uint32_t(*piece_length));
129
+ files.set_name(*name);
130
+
131
+ if (const auto* file_list = find(info, "files"); file_list && file_list->is_list()) {
132
+ // Multi-file: each entry has a length and a path component list.
133
+ for (const auto& entry : file_list->as_list()) {
134
+ const auto length = find_int(entry, "length");
135
+ const auto* path = find(entry, "path");
136
+ if (!length || *length < 0 || !path || !path->is_list()) return fail("bad file entry");
137
+ std::string rel = *name;
138
+ for (const auto& comp : path->as_list()) {
139
+ if (!comp.is_string() || !safe_component(comp.as_string())) return fail("unsafe file path");
140
+ rel += '/';
141
+ rel += comp.as_string();
142
+ }
143
+ if (!files.add_file(std::move(rel), *length)) return fail("file size overflow");
144
+ }
145
+ } else {
146
+ // Single-file: the info dict itself carries the length.
147
+ const auto length = find_int(info, "length");
148
+ if (!length || *length < 0) return fail("single-file torrent missing length");
149
+ if (!files.add_file(*name, *length)) return fail("file size overflow");
150
+ }
151
+
152
+ // The piece-hash count must match the file layout exactly.
153
+ if (pieces->size() / kInfoHashSize != files.num_pieces()) return fail("piece count mismatch");
154
+
155
+ files_ = std::move(files);
156
+ name_ = *name;
157
+ piece_hashes_.assign(pieces->begin(), pieces->end());
158
+ is_private_ = find_int(info, "private").value_or(0) == 1;
159
+ has_metadata_ = true;
160
+ return true;
161
+ }
162
+
163
+ std::optional<TorrentInfo> TorrentInfo::from_info_dict(const Bytes& info_dict_bytes,
164
+ const InfoHash& expected,
165
+ TorrentParseError* err) {
166
+ try {
167
+ librats::BencodeValue info = librats::BencodeDecoder::decode(info_dict_bytes);
168
+ TorrentInfo ti;
169
+ if (!ti.parse_info_dict(info, err)) return std::nullopt;
170
+ ti.info_dict_bytes_ = info_dict_bytes;
171
+ ti.info_hash_ = SHA1::hash_raw(info_dict_bytes.data(), info_dict_bytes.size());
172
+ if (!is_all_zero(expected) && ti.info_hash_ != expected) {
173
+ if (err) err->message = "info-hash mismatch";
174
+ return std::nullopt;
175
+ }
176
+ return ti;
177
+ } catch (const std::exception& e) {
178
+ if (err) err->message = e.what();
179
+ return std::nullopt;
180
+ }
181
+ }
182
+
183
+ std::optional<TorrentInfo> TorrentInfo::from_bytes(const Bytes& data, TorrentParseError* err) {
184
+ try {
185
+ librats::BencodeValue root = librats::BencodeDecoder::decode(data);
186
+ if (!root.is_dict()) { if (err) err->message = "torrent is not a dict"; return std::nullopt; }
187
+
188
+ const auto span = find_info_span(data);
189
+ const auto* info = find(root, "info");
190
+ if (!span || !info) { if (err) err->message = "missing info dict"; return std::nullopt; }
191
+
192
+ TorrentInfo ti;
193
+ if (!ti.parse_info_dict(*info, err)) return std::nullopt;
194
+
195
+ ti.info_dict_bytes_.assign(data.begin() + std::ptrdiff_t(span->first),
196
+ data.begin() + std::ptrdiff_t(span->second));
197
+ ti.info_hash_ = SHA1::hash_raw(ti.info_dict_bytes_.data(), ti.info_dict_bytes_.size());
198
+
199
+ // Optional top-level fields.
200
+ if (const auto* a = find_string(root, "announce")) ti.announce_ = *a;
201
+ if (const auto* c = find_string(root, "comment")) ti.comment_ = *c;
202
+ if (const auto* cb = find_string(root, "created by")) ti.created_by_ = *cb;
203
+ if (auto cd = find_int(root, "creation date")) ti.creation_date_ = *cd;
204
+
205
+ if (const auto* al = find(root, "announce-list"); al && al->is_list()) {
206
+ for (const auto& tier : al->as_list()) {
207
+ if (!tier.is_list()) continue;
208
+ TrackerTier t;
209
+ for (const auto& url : tier.as_list())
210
+ if (url.is_string()) t.push_back(url.as_string());
211
+ if (!t.empty()) ti.announce_list_.push_back(std::move(t));
212
+ }
213
+ }
214
+
215
+ if (const auto* ul = find(root, "url-list")) { // web seeds (BEP 19)
216
+ if (ul->is_string()) ti.web_seeds_.push_back(ul->as_string());
217
+ else if (ul->is_list())
218
+ for (const auto& u : ul->as_list())
219
+ if (u.is_string()) ti.web_seeds_.push_back(u.as_string());
220
+ }
221
+
222
+ if (const auto* nodes = find(root, "nodes"); nodes && nodes->is_list()) {
223
+ for (const auto& node : nodes->as_list()) {
224
+ if (!node.is_list() || node.as_list().size() != 2) continue;
225
+ const auto& pair = node.as_list();
226
+ if (pair[0].is_string() && pair[1].is_integer())
227
+ ti.dht_nodes_.push_back(DhtNode{pair[0].as_string(),
228
+ std::uint16_t(pair[1].as_integer())});
229
+ }
230
+ }
231
+
232
+ return ti;
233
+ } catch (const std::exception& e) {
234
+ if (err) err->message = e.what();
235
+ return std::nullopt;
236
+ }
237
+ }
238
+
239
+ std::optional<TorrentInfo> TorrentInfo::from_file(const std::string& path, TorrentParseError* err) {
240
+ std::size_t size = 0;
241
+ void* buf = read_file_binary(path.c_str(), &size);
242
+ if (!buf) { if (err) err->message = "cannot read file"; return std::nullopt; }
243
+ Bytes data(static_cast<std::uint8_t*>(buf), static_cast<std::uint8_t*>(buf) + size);
244
+ free_file_buffer(buf);
245
+ return from_bytes(data, err);
246
+ }
247
+
248
+ std::optional<TorrentInfo> TorrentInfo::from_magnet(const std::string& uri, TorrentParseError* err) {
249
+ auto magnet = MagnetUri::parse(uri);
250
+ if (!magnet) { if (err) err->message = "invalid magnet uri"; return std::nullopt; }
251
+
252
+ TorrentInfo ti;
253
+ ti.info_hash_ = magnet->info_hash;
254
+ ti.name_ = magnet->display_name;
255
+ ti.web_seeds_ = magnet->web_seeds;
256
+ ti.has_metadata_ = false; // file list & piece hashes still need fetching
257
+ for (const auto& tr : magnet->trackers) ti.announce_list_.push_back({tr});
258
+ if (!ti.announce_list_.empty() && !ti.announce_list_.front().empty())
259
+ ti.announce_ = ti.announce_list_.front().front();
260
+ return ti;
261
+ }
262
+
263
+ bool TorrentInfo::set_metadata(const Bytes& info_dict_bytes) {
264
+ auto completed = from_info_dict(info_dict_bytes, info_hash_, nullptr);
265
+ if (!completed) return false;
266
+ // Preserve discovery fields learned from the magnet; adopt the parsed payload.
267
+ files_ = std::move(completed->files_);
268
+ piece_hashes_ = std::move(completed->piece_hashes_);
269
+ info_dict_bytes_ = std::move(completed->info_dict_bytes_);
270
+ is_private_ = completed->is_private_;
271
+ if (name_.empty()) name_ = completed->name_;
272
+ has_metadata_ = true;
273
+ return true;
274
+ }
275
+
276
+ std::array<std::uint8_t, 20> TorrentInfo::piece_hash(std::uint32_t index) const {
277
+ std::array<std::uint8_t, 20> out{};
278
+ const std::size_t off = std::size_t(index) * kInfoHashSize;
279
+ if (off + kInfoHashSize <= piece_hashes_.size())
280
+ std::memcpy(out.data(), piece_hashes_.data() + off, kInfoHashSize);
281
+ return out;
282
+ }
283
+
284
+ std::vector<std::string> TorrentInfo::all_trackers() const {
285
+ std::vector<std::string> all;
286
+ if (!announce_.empty()) all.push_back(announce_);
287
+ for (const auto& tier : announce_list_)
288
+ for (const auto& url : tier)
289
+ if (url != announce_) all.push_back(url);
290
+ return all;
291
+ }
292
+
293
+ std::string TorrentInfo::to_magnet_uri(bool include_trackers) const {
294
+ std::string uri = "magnet:?xt=urn:btih:" + to_hex(info_hash_);
295
+ if (!name_.empty()) {
296
+ uri += "&dn=";
297
+ // Percent-encode anything that isn't an unreserved URI character.
298
+ for (unsigned char c : name_) {
299
+ if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
300
+ uri += char(c);
301
+ } else {
302
+ static const char hex[] = "0123456789ABCDEF";
303
+ uri += '%';
304
+ uri += hex[c >> 4];
305
+ uri += hex[c & 0x0F];
306
+ }
307
+ }
308
+ }
309
+ if (include_trackers)
310
+ for (const auto& tr : all_trackers()) uri += "&tr=" + tr;
311
+ return uri;
312
+ }
313
+
314
+ } // namespace librats::bittorrent
@@ -0,0 +1,118 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file torrent_info.h
5
+ * @brief Parsed torrent metadata: file layout, piece hashes, trackers, info-hash.
6
+ *
7
+ * A TorrentInfo is built from a .torrent file, a bare info dictionary (received
8
+ * via ut_metadata) or a magnet link. From a magnet it is "valid but without
9
+ * metadata" — it knows only the info-hash until set_metadata() completes it.
10
+ *
11
+ * The info-hash is the SHA-1 of the *exact* bytes of the bencoded `info`
12
+ * dictionary. We therefore keep those bytes verbatim (info_dict_bytes_) rather
13
+ * than re-encoding the parsed structure, so re-serving them over BEP 9 and
14
+ * re-hashing always reproduce the original hash.
15
+ */
16
+
17
+ #include "librats/util/rats_export.h"
18
+ #include "librats/bittorrent/file_storage.h"
19
+ #include "librats/bittorrent/types.h"
20
+ #include "librats/core/bytes.h"
21
+
22
+ #include <array>
23
+ #include <cstdint>
24
+ #include <optional>
25
+ #include <string>
26
+ #include <vector>
27
+
28
+ namespace librats { class BencodeValue; }
29
+
30
+ namespace librats::bittorrent {
31
+
32
+ /// Diagnostic for a parse failure (optional out-param of the factories).
33
+ struct TorrentParseError {
34
+ std::string message;
35
+ };
36
+
37
+ class RATS_API TorrentInfo {
38
+ public:
39
+ using TrackerTier = std::vector<std::string>;
40
+ struct DhtNode { std::string host; std::uint16_t port = 0; };
41
+
42
+ TorrentInfo() = default;
43
+
44
+ // ---- Factories ----
45
+ static std::optional<TorrentInfo> from_bytes(const Bytes& data, TorrentParseError* err = nullptr);
46
+ static std::optional<TorrentInfo> from_file(const std::string& path, TorrentParseError* err = nullptr);
47
+ static std::optional<TorrentInfo> from_magnet(const std::string& uri, TorrentParseError* err = nullptr);
48
+ /// Build directly from a bencoded info dict. If @p expected is non-zero, the
49
+ /// computed info-hash must match it.
50
+ static std::optional<TorrentInfo> from_info_dict(const Bytes& info_dict_bytes,
51
+ const InfoHash& expected,
52
+ TorrentParseError* err = nullptr);
53
+
54
+ /// Complete a magnet-built TorrentInfo with the info dict fetched from peers.
55
+ /// Returns false (leaving the object unchanged) if the bytes don't hash to
56
+ /// the expected info-hash or fail to parse.
57
+ bool set_metadata(const Bytes& info_dict_bytes);
58
+
59
+ // ---- Core properties ----
60
+ bool is_valid() const noexcept { return !is_all_zero(info_hash_); }
61
+ bool has_metadata() const noexcept { return has_metadata_; }
62
+
63
+ const InfoHash& info_hash() const noexcept { return info_hash_; }
64
+ std::string info_hash_hex() const { return to_hex(info_hash_); }
65
+ const std::string& name() const noexcept { return name_; }
66
+ const std::string& comment() const noexcept { return comment_; }
67
+ const std::string& created_by() const noexcept { return created_by_; }
68
+ std::int64_t creation_date()const noexcept { return creation_date_; }
69
+ bool is_private() const noexcept { return is_private_; }
70
+
71
+ // ---- File & piece info ----
72
+ const FileStorage& files() const noexcept { return files_; }
73
+ std::int64_t total_size() const noexcept { return files_.total_size(); }
74
+ std::size_t num_files() const noexcept { return files_.num_files(); }
75
+ std::uint32_t piece_length() const noexcept { return files_.piece_length(); }
76
+ std::uint32_t num_pieces() const noexcept { return files_.num_pieces(); }
77
+ std::uint32_t piece_size(std::uint32_t i) const noexcept { return files_.piece_size(i); }
78
+
79
+ /// 20-byte SHA-1 of piece @p index (all-zero if out of range).
80
+ std::array<std::uint8_t, 20> piece_hash(std::uint32_t index) const;
81
+ const Bytes& piece_hashes() const noexcept { return piece_hashes_; }
82
+
83
+ // ---- Trackers / seeds / nodes ----
84
+ const std::string& announce() const noexcept { return announce_; }
85
+ const std::vector<TrackerTier>& announce_list() const noexcept { return announce_list_; }
86
+ std::vector<std::string> all_trackers() const;
87
+ const std::vector<std::string>& web_seeds() const noexcept { return web_seeds_; }
88
+ const std::vector<DhtNode>& dht_nodes() const noexcept { return dht_nodes_; }
89
+
90
+ // ---- Raw info dict (for BEP 9) ----
91
+ const Bytes& info_dict_bytes() const noexcept { return info_dict_bytes_; }
92
+ std::size_t metadata_size() const noexcept { return info_dict_bytes_.size(); }
93
+
94
+ /// Build a magnet URI for this torrent.
95
+ std::string to_magnet_uri(bool include_trackers = true) const;
96
+
97
+ private:
98
+ /// Parse name/piece-length/pieces/files/private out of a bencoded info dict
99
+ /// into files_ and piece_hashes_. Does not touch info_hash_/info_dict_bytes_.
100
+ bool parse_info_dict(const librats::BencodeValue& info, TorrentParseError* err);
101
+
102
+ InfoHash info_hash_{};
103
+ std::string name_;
104
+ std::string comment_;
105
+ std::string created_by_;
106
+ std::int64_t creation_date_ = 0;
107
+ bool is_private_ = false;
108
+ bool has_metadata_ = false;
109
+ FileStorage files_;
110
+ Bytes piece_hashes_;
111
+ std::string announce_;
112
+ std::vector<TrackerTier> announce_list_;
113
+ std::vector<std::string> web_seeds_;
114
+ std::vector<DhtNode> dht_nodes_;
115
+ Bytes info_dict_bytes_;
116
+ };
117
+
118
+ } // namespace librats::bittorrent