librats 1.0.2 → 2.1.4

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 (313) hide show
  1. package/README.md +137 -332
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +271 -696
  4. package/lib/index.js +383 -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 +396 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +731 -0
  10. package/native-src/src/librats/bindings/rats.h +352 -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 +42 -0
  73. package/native-src/src/librats/core/types.h +93 -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/config.h +110 -0
  144. package/native-src/src/librats/node/dial_service.h +54 -0
  145. package/native-src/src/librats/node/dialer.cpp +264 -0
  146. package/native-src/src/librats/node/dialer.h +188 -0
  147. package/native-src/src/librats/node/host_events.h +26 -0
  148. package/native-src/src/librats/node/identify.cpp +130 -0
  149. package/native-src/src/librats/node/identify.h +71 -0
  150. package/native-src/src/librats/node/nat_status.cpp +103 -0
  151. package/native-src/src/librats/node/nat_status.h +118 -0
  152. package/native-src/src/librats/node/node.cpp +826 -0
  153. package/native-src/src/librats/node/node.h +333 -0
  154. package/native-src/src/librats/node/node_context.h +33 -0
  155. package/native-src/src/librats/node/peer_network.h +91 -0
  156. package/native-src/src/librats/peer/peer.h +49 -0
  157. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  158. package/native-src/src/librats/peer/peer_book.h +88 -0
  159. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  160. package/native-src/src/librats/peer/peer_id.h +62 -0
  161. package/native-src/src/librats/peer/peer_info.h +37 -0
  162. package/native-src/src/librats/peer/peer_table.cpp +137 -0
  163. package/native-src/src/librats/peer/peer_table.h +148 -0
  164. package/native-src/src/librats/security/handshaker.h +66 -0
  165. package/native-src/src/librats/security/identity.h +43 -0
  166. package/native-src/src/librats/security/noise_security.cpp +122 -0
  167. package/native-src/src/librats/security/noise_security.h +37 -0
  168. package/native-src/src/librats/security/plaintext_security.h +106 -0
  169. package/native-src/src/librats/security/session.h +37 -0
  170. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  171. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  172. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  173. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  174. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  176. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  177. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  178. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  179. package/native-src/src/librats/subsystems/hole_punch.cpp +532 -0
  180. package/native-src/src/librats/subsystems/hole_punch.h +266 -0
  181. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  182. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  184. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  185. package/native-src/src/librats/subsystems/message_json.h +88 -0
  186. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  188. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  189. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  190. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  192. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  193. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  194. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  195. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  196. package/native-src/src/librats/transport/connection.cpp +343 -0
  197. package/native-src/src/librats/transport/connection.h +283 -0
  198. package/native-src/src/librats/transport/link.h +96 -0
  199. package/native-src/src/librats/transport/reactor.cpp +540 -0
  200. package/native-src/src/librats/transport/reactor.h +224 -0
  201. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  202. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  203. package/native-src/src/librats/transport/tcp_link.h +43 -0
  204. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  205. package/native-src/src/librats/transport/udp_mux.h +363 -0
  206. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  207. package/native-src/src/librats/transport/udp_packet.h +190 -0
  208. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  209. package/native-src/src/librats/transport/udp_stream.h +614 -0
  210. package/native-src/src/librats/util/features.h.in +51 -0
  211. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  212. package/native-src/src/librats/util/fs.h +136 -0
  213. package/native-src/src/librats/util/json.cpp +1002 -0
  214. package/native-src/src/librats/util/json.h +444 -0
  215. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  216. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  217. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  218. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  219. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  220. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  221. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  222. package/native-src/src/librats/util/rats_export.h +69 -0
  223. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  224. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  225. package/native-src/src/librats/wire/frame.cpp +76 -0
  226. package/native-src/src/librats/wire/frame.h +110 -0
  227. package/native-src/src/librats/wire/message_router.cpp +45 -0
  228. package/native-src/src/librats/wire/message_router.h +47 -0
  229. package/package.json +5 -4
  230. package/scripts/build-librats.js +1 -0
  231. package/scripts/postinstall.js +3 -3
  232. package/scripts/prepare-package.js +4 -4
  233. package/scripts/verify-installation.js +63 -105
  234. package/src/librats_node.cpp +1042 -1324
  235. package/native-src/src/bencode.cpp +0 -485
  236. package/native-src/src/bencode.h +0 -145
  237. package/native-src/src/bittorrent.cpp +0 -14
  238. package/native-src/src/bittorrent.h +0 -74
  239. package/native-src/src/bt_bitfield.cpp +0 -372
  240. package/native-src/src/bt_bitfield.h +0 -316
  241. package/native-src/src/bt_choker.cpp +0 -228
  242. package/native-src/src/bt_choker.h +0 -147
  243. package/native-src/src/bt_client.cpp +0 -1047
  244. package/native-src/src/bt_client.h +0 -445
  245. package/native-src/src/bt_create_torrent.cpp +0 -677
  246. package/native-src/src/bt_create_torrent.h +0 -473
  247. package/native-src/src/bt_extension.cpp +0 -469
  248. package/native-src/src/bt_extension.h +0 -309
  249. package/native-src/src/bt_file_storage.cpp +0 -261
  250. package/native-src/src/bt_file_storage.h +0 -298
  251. package/native-src/src/bt_handshake.cpp +0 -134
  252. package/native-src/src/bt_handshake.h +0 -157
  253. package/native-src/src/bt_messages.cpp +0 -364
  254. package/native-src/src/bt_messages.h +0 -324
  255. package/native-src/src/bt_network.cpp +0 -1007
  256. package/native-src/src/bt_network.h +0 -417
  257. package/native-src/src/bt_peer_connection.cpp +0 -742
  258. package/native-src/src/bt_peer_connection.h +0 -592
  259. package/native-src/src/bt_piece_picker.cpp +0 -786
  260. package/native-src/src/bt_piece_picker.h +0 -473
  261. package/native-src/src/bt_resume_data.cpp +0 -410
  262. package/native-src/src/bt_resume_data.h +0 -249
  263. package/native-src/src/bt_torrent.cpp +0 -2120
  264. package/native-src/src/bt_torrent.h +0 -641
  265. package/native-src/src/bt_torrent_info.cpp +0 -659
  266. package/native-src/src/bt_torrent_info.h +0 -418
  267. package/native-src/src/bt_types.h +0 -621
  268. package/native-src/src/chained_send_buffer.cpp +0 -75
  269. package/native-src/src/chained_send_buffer.h +0 -137
  270. package/native-src/src/crypto/hkdf.c +0 -266
  271. package/native-src/src/crypto/poly1305.h +0 -36
  272. package/native-src/src/dht.cpp +0 -3311
  273. package/native-src/src/dht.h +0 -717
  274. package/native-src/src/disk_io.cpp +0 -632
  275. package/native-src/src/disk_io.h +0 -315
  276. package/native-src/src/file_transfer.cpp +0 -1415
  277. package/native-src/src/file_transfer.h +0 -286
  278. package/native-src/src/fs.h +0 -108
  279. package/native-src/src/gossipsub.cpp +0 -1139
  280. package/native-src/src/gossipsub.h +0 -403
  281. package/native-src/src/ice.cpp +0 -893
  282. package/native-src/src/ice.h +0 -559
  283. package/native-src/src/json.hpp +0 -25526
  284. package/native-src/src/librats.cpp +0 -2378
  285. package/native-src/src/librats.h +0 -2324
  286. package/native-src/src/librats_bittorrent.cpp +0 -601
  287. package/native-src/src/librats_c.cpp +0 -1557
  288. package/native-src/src/librats_c.h +0 -323
  289. package/native-src/src/librats_discovery.cpp +0 -402
  290. package/native-src/src/librats_encryption.cpp +0 -275
  291. package/native-src/src/librats_file_transfer.cpp +0 -144
  292. package/native-src/src/librats_gossipsub.cpp +0 -289
  293. package/native-src/src/librats_ice.cpp +0 -213
  294. package/native-src/src/librats_log_macros.h +0 -36
  295. package/native-src/src/librats_logging.cpp +0 -173
  296. package/native-src/src/librats_mdns.cpp +0 -166
  297. package/native-src/src/librats_persistence.cpp +0 -796
  298. package/native-src/src/librats_portmap.cpp +0 -419
  299. package/native-src/src/librats_reconnection.cpp +0 -218
  300. package/native-src/src/librats_statistic.cpp +0 -105
  301. package/native-src/src/librats_storage.cpp +0 -189
  302. package/native-src/src/rats_export.h +0 -17
  303. package/native-src/src/receive_buffer.cpp +0 -82
  304. package/native-src/src/receive_buffer.h +0 -127
  305. package/native-src/src/socket.h +0 -228
  306. package/native-src/src/threadmanager.cpp +0 -105
  307. package/native-src/src/threadmanager.h +0 -53
  308. package/native-src/src/tracker.cpp +0 -1264
  309. package/native-src/src/tracker.h +0 -319
  310. package/native-src/src/turn.cpp +0 -762
  311. package/native-src/src/turn.h +0 -460
  312. package/native-src/src/wakeup_pipe.h +0 -60
  313. /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