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
@@ -1,1047 +0,0 @@
1
- #include "bt_client.h"
2
- #include "tracker.h"
3
- #include "logger.h"
4
- #include "fs.h"
5
-
6
- #include <chrono>
7
- #include <algorithm>
8
-
9
- namespace librats {
10
-
11
- //=============================================================================
12
- // Constructor / Destructor
13
- //=============================================================================
14
-
15
- BtClient::BtClient()
16
- : peer_id_(generate_peer_id("-LR0001-"))
17
- , running_(false)
18
- , dht_running_(false) {
19
- }
20
-
21
- BtClient::BtClient(const BtClientConfig& config)
22
- : config_(config)
23
- , peer_id_(generate_peer_id("-LR0001-"))
24
- , running_(false)
25
- , dht_running_(false) {
26
- }
27
-
28
- BtClient::~BtClient() {
29
- stop();
30
- }
31
-
32
- void BtClient::set_external_dht(DhtClient* dht) {
33
- std::lock_guard<std::mutex> lock(mutex_);
34
- if (running_) {
35
- LOG_WARN("BtClient", "Cannot set external DHT while client is running");
36
- return;
37
- }
38
- external_dht_ = dht;
39
- LOG_DEBUG("BtClient", "External DHT client set");
40
- }
41
-
42
- //=============================================================================
43
- // Lifecycle
44
- //=============================================================================
45
-
46
- void BtClient::start() {
47
- std::lock_guard<std::mutex> lock(mutex_);
48
-
49
- if (running_) return;
50
-
51
- running_ = true;
52
-
53
- // Initialize network manager
54
- BtNetworkConfig net_config;
55
- net_config.listen_port = config_.listen_port;
56
- net_config.max_connections = config_.max_connections;
57
- net_config.enable_incoming = true;
58
- net_config.peer_id = peer_id_; // For incoming connections
59
-
60
- network_manager_ = std::make_unique<BtNetworkManager>(net_config);
61
-
62
- // Set network callbacks
63
- network_manager_->set_connected_callback(
64
- [this](const BtInfoHash& hash, std::shared_ptr<BtPeerConnection> conn,
65
- socket_t sock, bool incoming) {
66
- on_peer_connected(hash, conn, sock, incoming);
67
- }
68
- );
69
-
70
- network_manager_->set_disconnected_callback(
71
- [this](const BtInfoHash& hash, BtPeerConnection* conn) {
72
- on_peer_disconnected(hash, conn);
73
- }
74
- );
75
-
76
- network_manager_->set_data_callback(
77
- [this](const BtInfoHash& hash, BtPeerConnection* conn, socket_t sock) {
78
- on_peer_data(hash, conn, sock);
79
- }
80
- );
81
-
82
- if (!network_manager_->start()) {
83
- LOG_ERROR("BtClient", "Failed to start network manager");
84
- running_ = false;
85
- return;
86
- }
87
-
88
- LOG_INFO("BtClient", "Network manager started, listening on port " +
89
- std::to_string(network_manager_->listen_port()));
90
-
91
- // Start DHT if enabled
92
- if (config_.enable_dht) {
93
- if (external_dht_) {
94
- // Use external DHT client - check if it's already running
95
- if (external_dht_->is_running()) {
96
- dht_running_ = true;
97
- LOG_INFO("BtClient", "Using external DHT client with " +
98
- std::to_string(external_dht_->get_routing_table_size()) + " nodes");
99
- } else {
100
- LOG_WARN("BtClient", "External DHT client provided but not running");
101
- }
102
- } else {
103
- // Create and start our own DHT client
104
- dht_client_ = std::make_unique<DhtClient>(config_.listen_port, "");
105
-
106
- if (dht_client_->start()) {
107
- // Bootstrap with default nodes
108
- auto bootstrap_nodes = DhtClient::get_default_bootstrap_nodes();
109
- dht_client_->bootstrap(bootstrap_nodes);
110
- dht_running_ = true;
111
-
112
- LOG_INFO("BtClient", "DHT started with " +
113
- std::to_string(bootstrap_nodes.size()) + " bootstrap nodes");
114
- } else {
115
- LOG_ERROR("BtClient", "Failed to start DHT");
116
- }
117
- }
118
- }
119
-
120
- // Initialize timing
121
- last_dht_announce_ = std::chrono::steady_clock::now();
122
- last_tracker_announce_ = std::chrono::steady_clock::now();
123
-
124
- // Start tick thread
125
- tick_thread_ = std::thread(&BtClient::tick_loop, this);
126
-
127
- if (on_alert_) {
128
- on_alert_("BitTorrent client started on port " +
129
- std::to_string(network_manager_->listen_port()));
130
- }
131
- }
132
-
133
- void BtClient::set_resume_data_path(const std::string& path) {
134
- std::lock_guard<std::mutex> lock(mutex_);
135
- config_.resume_data_path = path;
136
- LOG_INFO("BtClient", "Resume data path set to: " + path);
137
- }
138
-
139
- void BtClient::stop() {
140
- {
141
- std::lock_guard<std::mutex> lock(mutex_);
142
-
143
- if (!running_) return;
144
-
145
- running_ = false;
146
- dht_running_ = false;
147
-
148
- // Save resume data for all torrents before stopping
149
- LOG_INFO("BtClient", "Saving resume data before shutdown...");
150
- for (auto& [hash, torrent] : torrents_) {
151
- if (torrent->has_metadata()) {
152
- torrent->save_resume_data();
153
- }
154
- }
155
-
156
- // Stop all torrents
157
- for (auto& [hash, torrent] : torrents_) {
158
- torrent->stop();
159
- }
160
- }
161
-
162
- // Wait for tick thread
163
- if (tick_thread_.joinable()) {
164
- tick_thread_.join();
165
- }
166
-
167
- // Stop network manager
168
- if (network_manager_) {
169
- network_manager_->stop();
170
- network_manager_.reset();
171
- }
172
-
173
- // Stop DHT (only if we own it - not external)
174
- if (dht_client_) {
175
- dht_client_->stop();
176
- dht_client_.reset();
177
- }
178
- // Note: external_dht_ is not stopped - lifecycle managed by caller
179
-
180
- // Clear tracker managers
181
- tracker_managers_.clear();
182
-
183
- // Clear torrents (already stopped above)
184
- {
185
- std::lock_guard<std::mutex> lock(mutex_);
186
- torrents_.clear();
187
- }
188
-
189
- // Clear pending removals
190
- {
191
- std::lock_guard<std::mutex> lock(removal_mutex_);
192
- pending_removals_.clear();
193
- }
194
-
195
- if (on_alert_) {
196
- on_alert_("BitTorrent client stopped");
197
- }
198
- }
199
-
200
- //=============================================================================
201
- // Torrent Management
202
- //=============================================================================
203
-
204
- Torrent::Ptr BtClient::add_torrent_file(const std::string& path,
205
- const std::string& save_path) {
206
- auto info = TorrentInfo::from_file(path);
207
- if (!info) {
208
- if (on_alert_) {
209
- on_alert_("Failed to parse torrent file: " + path);
210
- }
211
- return nullptr;
212
- }
213
-
214
- return add_torrent(*info, save_path);
215
- }
216
-
217
- Torrent::Ptr BtClient::add_torrent(const TorrentInfo& info,
218
- const std::string& save_path) {
219
- std::lock_guard<std::mutex> lock(mutex_);
220
-
221
- // Check if already exists
222
- auto it = torrents_.find(info.info_hash());
223
- if (it != torrents_.end()) {
224
- return it->second;
225
- }
226
-
227
- auto torrent = create_torrent(info, save_path);
228
- torrents_[info.info_hash()] = torrent;
229
-
230
- // Register with network manager
231
- if (network_manager_ && info.has_metadata()) {
232
- network_manager_->register_torrent(info.info_hash(), peer_id_, info.num_pieces());
233
- }
234
-
235
- // Create tracker manager
236
- tracker_managers_[info.info_hash()] = std::make_unique<TrackerManager>(info);
237
-
238
- // Check if files already exist before starting - to avoid overwriting existing data
239
- // This is similar to libtorrent's async_check_files behavior
240
- bool files_exist = false;
241
- std::string effective_save_path = save_path.empty() ? config_.download_path : save_path;
242
- const auto& files = info.files().files();
243
- for (const auto& f : files) {
244
- std::string file_path = combine_paths(effective_save_path, f.path);
245
- if (file_exists(file_path)) {
246
- files_exist = true;
247
- break;
248
- }
249
- }
250
-
251
- if (running_) {
252
- if (files_exist) {
253
- // Files exist - check them before starting download to avoid overwriting
254
- LOG_INFO("BtClient", "Files exist for " + info.name() +
255
- ", checking before download to avoid overwriting...");
256
- torrent->check_files_async(
257
- [this, hash = info.info_hash()](uint32_t have, uint32_t total) {
258
- LOG_INFO("BtClient", "File check complete: " +
259
- std::to_string(have) + "/" + std::to_string(total) + " pieces");
260
-
261
- auto t = get_torrent(hash);
262
- if (t) {
263
- // Now start the torrent after file check completes
264
- t->start();
265
-
266
- // Announce after start
267
- if (dht_running_) {
268
- find_peers_dht(hash);
269
- }
270
-
271
- // Save resume data after check
272
- t->save_resume_data();
273
- }
274
- },
275
- nullptr
276
- );
277
- } else {
278
- // No files exist - safe to start download immediately
279
- torrent->start();
280
-
281
- // Announce to DHT
282
- if (dht_running_) {
283
- find_peers_dht(info.info_hash());
284
- }
285
- }
286
-
287
- // Announce to trackers (regardless of file check status)
288
- auto& tracker_mgr = tracker_managers_[info.info_hash()];
289
- if (tracker_mgr) {
290
- TrackerRequest req;
291
- req.info_hash = info.info_hash();
292
- req.peer_id = peer_id_;
293
- req.port = network_manager_ ? network_manager_->listen_port() : config_.listen_port;
294
- req.uploaded = 0;
295
- req.downloaded = 0;
296
- req.left = info.total_size();
297
- req.event = TrackerEvent::STARTED;
298
- req.numwant = 50;
299
-
300
- tracker_mgr->announce(req, [this, hash = info.info_hash()](
301
- const TrackerResponse& response, const std::string& tracker_url) {
302
-
303
- if (response.success && !response.peers.empty()) {
304
- LOG_INFO("BtClient", "Got " + std::to_string(response.peers.size()) +
305
- " peers from tracker " + tracker_url);
306
-
307
- auto torrent = get_torrent(hash);
308
- if (torrent) {
309
- for (const auto& peer : response.peers) {
310
- torrent->add_peer(peer.ip, peer.port);
311
- }
312
- }
313
- }
314
- });
315
- }
316
- }
317
-
318
- if (on_torrent_added_) {
319
- on_torrent_added_(torrent);
320
- }
321
-
322
- LOG_INFO("BtClient", "Added torrent: " + info.name() + " (" +
323
- info_hash_to_hex(info.info_hash()).substr(0, 8) + "...)");
324
-
325
- return torrent;
326
- }
327
-
328
- Torrent::Ptr BtClient::add_magnet(const std::string& magnet_uri,
329
- const std::string& save_path,
330
- bool skip_dht_search) {
331
- auto info = TorrentInfo::from_magnet(magnet_uri);
332
- if (!info) {
333
- if (on_alert_) {
334
- on_alert_("Failed to parse magnet URI");
335
- }
336
- return nullptr;
337
- }
338
-
339
- std::lock_guard<std::mutex> lock(mutex_);
340
-
341
- // Check if already exists
342
- auto it = torrents_.find(info->info_hash());
343
- if (it != torrents_.end()) {
344
- return it->second;
345
- }
346
-
347
- // Create torrent without full metadata
348
- TorrentConfig torrent_config;
349
- // For metadata-only mode (skip_dht_search=true with empty save_path), keep save_path empty
350
- // Otherwise use default download path if not specified
351
- if (skip_dht_search && save_path.empty()) {
352
- // Metadata-only mode - keep empty save_path for proper detection
353
- torrent_config.save_path = "";
354
- } else {
355
- torrent_config.save_path = save_path.empty() ? config_.download_path : save_path;
356
- }
357
- torrent_config.resume_data_path = config_.resume_data_path;
358
- torrent_config.max_connections = config_.max_connections_per_torrent;
359
- torrent_config.max_uploads = config_.max_uploads;
360
-
361
- auto torrent = std::make_shared<Torrent>(
362
- info->info_hash(),
363
- info->name(),
364
- torrent_config,
365
- peer_id_
366
- );
367
-
368
- torrents_[info->info_hash()] = torrent;
369
-
370
- if (running_) {
371
- torrent->start();
372
-
373
- // Find peers via DHT for metadata (skip if using direct peer connection)
374
- if (dht_running_ && !skip_dht_search) {
375
- find_peers_dht(info->info_hash());
376
- }
377
- }
378
-
379
- if (on_torrent_added_) {
380
- on_torrent_added_(torrent);
381
- }
382
-
383
- LOG_INFO("BtClient", "Added magnet: " + info->name() + " (" +
384
- info_hash_to_hex(info->info_hash()).substr(0, 8) + "...)");
385
-
386
- return torrent;
387
- }
388
-
389
- Torrent::Ptr BtClient::add_torrent_with_resume(
390
- const TorrentInfo& info,
391
- const TorrentResumeData& resume_data,
392
- const std::string& save_path) {
393
-
394
- std::lock_guard<std::mutex> lock(mutex_);
395
-
396
- // Check if already exists
397
- auto it = torrents_.find(info.info_hash());
398
- if (it != torrents_.end()) {
399
- return it->second;
400
- }
401
-
402
- // Create torrent with resume-specified or provided save path
403
- std::string effective_save_path = save_path.empty()
404
- ? (resume_data.save_path.empty() ? config_.download_path : resume_data.save_path)
405
- : save_path;
406
-
407
- auto torrent = create_torrent(info, effective_save_path);
408
-
409
- // Load resume data before starting
410
- if (!torrent->load_resume_data(resume_data)) {
411
- LOG_WARN("BtClient", "Failed to load resume data for " + info.name());
412
- } else {
413
- LOG_INFO("BtClient", "Loaded resume data for " + info.name() +
414
- ": " + std::to_string(resume_data.have_pieces.count()) + " pieces");
415
- }
416
-
417
- torrents_[info.info_hash()] = torrent;
418
-
419
- // Register with network manager
420
- if (network_manager_ && info.has_metadata()) {
421
- network_manager_->register_torrent(info.info_hash(), peer_id_, info.num_pieces());
422
- }
423
-
424
- // Create tracker manager
425
- tracker_managers_[info.info_hash()] = std::make_unique<TrackerManager>(info);
426
-
427
- if (running_) {
428
- torrent->start();
429
-
430
- // Announce to DHT
431
- if (dht_running_) {
432
- find_peers_dht(info.info_hash());
433
- }
434
- }
435
-
436
- if (on_torrent_added_) {
437
- on_torrent_added_(torrent);
438
- }
439
-
440
- LOG_INFO("BtClient", "Added torrent with resume: " + info.name() + " (" +
441
- info_hash_to_hex(info.info_hash()).substr(0, 8) + "...)");
442
-
443
- return torrent;
444
- }
445
-
446
- Torrent::Ptr BtClient::add_torrent_auto_resume(
447
- const TorrentInfo& info,
448
- const std::string& save_path,
449
- bool check_files) {
450
-
451
- std::string effective_save_path = save_path.empty() ? config_.download_path : save_path;
452
-
453
- // Try to load resume data
454
- std::string resume_path = get_resume_file_path(effective_save_path, info.info_hash());
455
- std::string error;
456
- auto resume_data = read_resume_data_file(resume_path, &error);
457
-
458
- if (resume_data) {
459
- LOG_INFO("BtClient", "Found resume data for " + info.name());
460
- return add_torrent_with_resume(info, *resume_data, effective_save_path);
461
- }
462
-
463
- LOG_DEBUG("BtClient", "No resume data for " + info.name() + ": " + error);
464
-
465
- // Add torrent normally
466
- auto torrent = add_torrent(info, effective_save_path);
467
-
468
- // If files might exist, check them
469
- if (torrent && check_files) {
470
- // Check if any files already exist
471
- bool files_exist = false;
472
- const auto& files = info.files().files();
473
- for (const auto& f : files) {
474
- std::string file_path = combine_paths(effective_save_path, f.path);
475
- if (file_exists(file_path)) {
476
- files_exist = true;
477
- break;
478
- }
479
- }
480
-
481
- if (files_exist) {
482
- LOG_INFO("BtClient", "Files exist for " + info.name() + ", checking...");
483
- torrent->check_files_async(
484
- [this, hash = info.info_hash()](uint32_t have, uint32_t total) {
485
- LOG_INFO("BtClient", "File check complete: " +
486
- std::to_string(have) + "/" + std::to_string(total) + " pieces");
487
-
488
- // Save resume data after check
489
- auto t = get_torrent(hash);
490
- if (t) {
491
- t->save_resume_data();
492
- }
493
- },
494
- nullptr
495
- );
496
- }
497
- }
498
-
499
- return torrent;
500
- }
501
-
502
- Torrent::Ptr BtClient::add_torrent_for_seeding(
503
- const TorrentInfo& info,
504
- const std::string& save_path) {
505
-
506
- std::lock_guard<std::mutex> lock(mutex_);
507
-
508
- // Check if already exists
509
- auto it = torrents_.find(info.info_hash());
510
- if (it != torrents_.end()) {
511
- return it->second;
512
- }
513
-
514
- LOG_INFO("BtClient", "Adding torrent for seeding: " + info.name());
515
-
516
- // Create torrent with seed_mode enabled
517
- TorrentConfig torrent_config;
518
- torrent_config.save_path = save_path.empty() ? config_.download_path : save_path;
519
- torrent_config.resume_data_path = config_.resume_data_path;
520
- torrent_config.max_connections = config_.max_connections_per_torrent;
521
- torrent_config.max_uploads = config_.max_uploads;
522
- torrent_config.seed_mode = true; // Key difference: seed_mode enabled
523
-
524
- auto torrent = std::make_shared<Torrent>(info, torrent_config, peer_id_);
525
- torrents_[info.info_hash()] = torrent;
526
-
527
- // Register with network manager
528
- if (network_manager_ && info.has_metadata()) {
529
- network_manager_->register_torrent(info.info_hash(), peer_id_, info.num_pieces());
530
- }
531
-
532
- // Create tracker manager
533
- tracker_managers_[info.info_hash()] = std::make_unique<TrackerManager>(info);
534
-
535
- if (running_) {
536
- torrent->start();
537
-
538
- // Announce to DHT
539
- if (dht_running_) {
540
- find_peers_dht(info.info_hash());
541
- }
542
-
543
- // Announce to trackers with completed event (since we're seeding)
544
- auto& tracker_mgr = tracker_managers_[info.info_hash()];
545
- if (tracker_mgr) {
546
- TrackerRequest req;
547
- req.info_hash = info.info_hash();
548
- req.peer_id = peer_id_;
549
- req.port = network_manager_ ? network_manager_->listen_port() : config_.listen_port;
550
- req.uploaded = 0;
551
- req.downloaded = 0;
552
- req.left = 0; // 0 = seeding
553
- req.event = TrackerEvent::COMPLETED;
554
- req.numwant = 50;
555
-
556
- tracker_mgr->announce(req, [this, hash = info.info_hash()](
557
- const TrackerResponse& response, const std::string& tracker_url) {
558
-
559
- if (response.success && !response.peers.empty()) {
560
- LOG_INFO("BtClient", "Got " + std::to_string(response.peers.size()) +
561
- " peers from tracker " + tracker_url);
562
-
563
- auto torrent = get_torrent(hash);
564
- if (torrent) {
565
- for (const auto& peer : response.peers) {
566
- torrent->add_peer(peer.ip, peer.port);
567
- }
568
- }
569
- }
570
- });
571
- }
572
- }
573
-
574
- if (on_torrent_added_) {
575
- on_torrent_added_(torrent);
576
- }
577
-
578
- LOG_INFO("BtClient", "Added torrent for seeding: " + info.name() + " (" +
579
- info_hash_to_hex(info.info_hash()).substr(0, 8) + "...)");
580
-
581
- return torrent;
582
- }
583
-
584
- void BtClient::save_all_resume_data() {
585
- std::lock_guard<std::mutex> lock(mutex_);
586
-
587
- LOG_INFO("BtClient", "Saving resume data for " + std::to_string(torrents_.size()) + " torrents");
588
-
589
- for (auto& [hash, torrent] : torrents_) {
590
- if (torrent->has_metadata()) {
591
- torrent->save_resume_data();
592
- }
593
- }
594
- }
595
-
596
- void BtClient::remove_torrent(const BtInfoHash& info_hash, bool delete_files) {
597
- Torrent::Ptr torrent;
598
-
599
- {
600
- std::lock_guard<std::mutex> lock(mutex_);
601
-
602
- LOG_INFO("BtClient", "Removing torrent: " + info_hash_to_hex(info_hash).substr(0, 8) + "...");
603
-
604
- auto it = torrents_.find(info_hash);
605
- if (it == torrents_.end()) {
606
- return;
607
- }
608
-
609
- torrent = it->second; // Keep a reference before erasing
610
- torrent->stop();
611
-
612
- // Remove tracker manager
613
- tracker_managers_.erase(info_hash);
614
-
615
- if (delete_files) {
616
- // TODO: Delete downloaded files
617
- }
618
-
619
- torrents_.erase(it);
620
- }
621
- // mutex_ is now RELEASED - important to avoid deadlock!
622
- // The callbacks below may try to acquire mutex_ via get_torrent()
623
-
624
- // Unregister from network manager OUTSIDE the mutex
625
- // This invokes on_disconnected_ callbacks which call on_peer_disconnected()
626
- // which needs to acquire mutex_ via get_torrent()
627
- if (network_manager_) {
628
- network_manager_->unregister_torrent(info_hash);
629
- }
630
-
631
- // Cancel any active DHT searches/announces for this torrent
632
- DhtClient* dht = external_dht_ ? external_dht_ : dht_client_.get();
633
- if (dht) {
634
- dht->cancel_search(info_hash);
635
- }
636
-
637
- if (on_torrent_removed_) {
638
- on_torrent_removed_(info_hash);
639
- }
640
-
641
- LOG_INFO("BtClient", "Removed torrent: " + info_hash_to_hex(info_hash).substr(0, 8) + "...");
642
- }
643
-
644
- void BtClient::mark_for_removal(const BtInfoHash& info_hash) {
645
- std::lock_guard<std::mutex> lock(removal_mutex_);
646
-
647
- // Avoid duplicates
648
- for (const auto& hash : pending_removals_) {
649
- if (hash == info_hash) {
650
- return;
651
- }
652
- }
653
-
654
- pending_removals_.push_back(info_hash);
655
- LOG_DEBUG("BtClient", "Marked torrent for removal: " +
656
- info_hash_to_hex(info_hash).substr(0, 8) + "...");
657
- }
658
-
659
- Torrent::Ptr BtClient::get_torrent(const BtInfoHash& info_hash) {
660
- std::lock_guard<std::mutex> lock(mutex_);
661
-
662
- auto it = torrents_.find(info_hash);
663
- if (it != torrents_.end()) {
664
- return it->second;
665
- }
666
- return nullptr;
667
- }
668
-
669
- std::vector<Torrent::Ptr> BtClient::get_torrents() {
670
- std::lock_guard<std::mutex> lock(mutex_);
671
-
672
- std::vector<Torrent::Ptr> result;
673
- result.reserve(torrents_.size());
674
- for (const auto& [hash, torrent] : torrents_) {
675
- result.push_back(torrent);
676
- }
677
- return result;
678
- }
679
-
680
- size_t BtClient::num_torrents() const {
681
- std::lock_guard<std::mutex> lock(mutex_);
682
- return torrents_.size();
683
- }
684
-
685
- //=============================================================================
686
- // Configuration
687
- //=============================================================================
688
-
689
- void BtClient::set_download_limit(uint64_t bytes_per_sec) {
690
- std::lock_guard<std::mutex> lock(mutex_);
691
- config_.download_limit = bytes_per_sec;
692
- }
693
-
694
- void BtClient::set_upload_limit(uint64_t bytes_per_sec) {
695
- std::lock_guard<std::mutex> lock(mutex_);
696
- config_.upload_limit = bytes_per_sec;
697
- }
698
-
699
- //=============================================================================
700
- // Statistics
701
- //=============================================================================
702
-
703
- uint64_t BtClient::total_download_rate() const {
704
- std::lock_guard<std::mutex> lock(mutex_);
705
-
706
- uint64_t total = 0;
707
- for (const auto& [hash, torrent] : torrents_) {
708
- total += torrent->stats().download_rate;
709
- }
710
- return total;
711
- }
712
-
713
- uint64_t BtClient::total_upload_rate() const {
714
- std::lock_guard<std::mutex> lock(mutex_);
715
-
716
- uint64_t total = 0;
717
- for (const auto& [hash, torrent] : torrents_) {
718
- total += torrent->stats().upload_rate;
719
- }
720
- return total;
721
- }
722
-
723
- size_t BtClient::total_peers() const {
724
- std::lock_guard<std::mutex> lock(mutex_);
725
-
726
- size_t total = 0;
727
- for (const auto& [hash, torrent] : torrents_) {
728
- total += torrent->num_peers();
729
- }
730
- return total;
731
- }
732
-
733
- //=============================================================================
734
- // DHT
735
- //=============================================================================
736
-
737
- void BtClient::add_dht_node(const std::string& host, uint16_t port) {
738
- DhtClient* dht = external_dht_ ? external_dht_ : dht_client_.get();
739
- if (dht) {
740
- std::vector<Peer> nodes = {{host, port}};
741
- dht->bootstrap(nodes);
742
- }
743
- }
744
-
745
- size_t BtClient::dht_node_count() const {
746
- DhtClient* dht = external_dht_ ? external_dht_ : dht_client_.get();
747
- if (dht) {
748
- return dht->get_routing_table_size();
749
- }
750
- return 0;
751
- }
752
-
753
- void BtClient::announce_to_dht(const BtInfoHash& info_hash) {
754
- DhtClient* dht = external_dht_ ? external_dht_ : dht_client_.get();
755
- if (!dht || !dht_running_) {
756
- return;
757
- }
758
-
759
- // Convert BtInfoHash to InfoHash (they're the same type)
760
- InfoHash dht_hash;
761
- std::copy(info_hash.begin(), info_hash.end(), dht_hash.begin());
762
-
763
- uint16_t port = network_manager_ ? network_manager_->listen_port() : config_.listen_port;
764
-
765
- dht->announce_peer(dht_hash, port,
766
- [this, info_hash](const std::vector<Peer>& peers, const InfoHash&) {
767
- on_dht_peers_found(peers, info_hash);
768
- }
769
- );
770
- }
771
-
772
- void BtClient::find_peers_dht(const BtInfoHash& info_hash) {
773
- DhtClient* dht = external_dht_ ? external_dht_ : dht_client_.get();
774
- if (!dht || !dht_running_) {
775
- return;
776
- }
777
-
778
- InfoHash dht_hash;
779
- std::copy(info_hash.begin(), info_hash.end(), dht_hash.begin());
780
-
781
- dht->find_peers(dht_hash,
782
- [this, info_hash](const std::vector<Peer>& peers, const InfoHash&) {
783
- on_dht_peers_found(peers, info_hash);
784
- }
785
- );
786
-
787
- LOG_DEBUG("BtClient", "Started DHT peer search for " +
788
- info_hash_to_hex(info_hash).substr(0, 8) + "...");
789
- }
790
-
791
- void BtClient::on_dht_peers_found(const std::vector<Peer>& peers, const InfoHash& info_hash) {
792
- if (peers.empty()) {
793
- return;
794
- }
795
-
796
- // Build peer list string for logging (show up to 5 peers)
797
- std::string peer_list;
798
- size_t show_count = (std::min)(peers.size(), static_cast<size_t>(5));
799
- for (size_t i = 0; i < show_count; ++i) {
800
- if (i > 0) peer_list += ", ";
801
- peer_list += peers[i].ip + ":" + std::to_string(peers[i].port);
802
- }
803
- if (peers.size() > 5) {
804
- peer_list += ", ...";
805
- }
806
-
807
- LOG_INFO("BtClient", "DHT found " + std::to_string(peers.size()) + " peers for " +
808
- node_id_to_hex(info_hash).substr(0, 8) + "...: " + peer_list);
809
-
810
- BtInfoHash bt_hash;
811
- std::copy(info_hash.begin(), info_hash.end(), bt_hash.begin());
812
-
813
- auto torrent = get_torrent(bt_hash);
814
- if (torrent) {
815
- for (const auto& peer : peers) {
816
- torrent->add_peer(peer.ip, peer.port);
817
- }
818
- }
819
- }
820
-
821
- //=============================================================================
822
- // Internal Methods - Network Callbacks
823
- //=============================================================================
824
-
825
- void BtClient::on_peer_connected(const BtInfoHash& info_hash,
826
- std::shared_ptr<BtPeerConnection> connection,
827
- socket_t socket, bool is_incoming) {
828
- auto torrent = get_torrent(info_hash);
829
- if (!torrent) {
830
- LOG_DEBUG("BtClient", "Peer connected for unknown torrent");
831
- if (network_manager_) {
832
- network_manager_->close_connection(socket);
833
- }
834
- return;
835
- }
836
-
837
- LOG_INFO("BtClient", "Peer connected: " + connection->ip() + ":" +
838
- std::to_string(connection->port()) +
839
- (is_incoming ? " (incoming)" : " (outgoing)") +
840
- " state=" + peer_state_to_string(connection->state()));
841
-
842
- // NOTE: Do NOT call set_socket() here! The socket was already set by
843
- // NetworkManager when the connection was created. Calling set_socket()
844
- // again would reset the state from Connected back to Handshaking,
845
- // breaking the torrent download logic.
846
-
847
- // Hand connection to torrent - it will manage from here
848
- // Both network manager and torrent share ownership via shared_ptr
849
- // Torrent will setup callbacks and send extension handshake
850
- torrent->add_connection(connection);
851
- }
852
-
853
- void BtClient::on_peer_disconnected(const BtInfoHash& info_hash,
854
- BtPeerConnection* connection) {
855
- LOG_DEBUG("BtClient", "Peer disconnected: " + connection->ip());
856
-
857
- // Remove connection from torrent
858
- auto torrent = get_torrent(info_hash);
859
- if (torrent) {
860
- torrent->remove_connection(connection);
861
- }
862
- }
863
-
864
- void BtClient::on_peer_data(const BtInfoHash& info_hash,
865
- BtPeerConnection* connection,
866
- socket_t socket) {
867
- // Data is already processed by connection->process_incoming()
868
- // Send data is already in connection->send_buffer() and will be
869
- // sent by NetworkManager's flush_send_buffer() - no action needed here
870
- (void)info_hash;
871
- (void)connection;
872
- (void)socket;
873
- }
874
-
875
- void BtClient::connect_pending_peers() {
876
- if (!network_manager_) return;
877
-
878
- std::lock_guard<std::mutex> lock(mutex_);
879
-
880
- for (auto& [hash, torrent] : torrents_) {
881
- if (!torrent->is_active()) continue;
882
- if (!torrent->has_metadata()) continue;
883
-
884
- // Get pending peers from torrent and initiate connections
885
- // The torrent stores pending_peers_ internally
886
- // We need to expose them or have torrent request connections
887
-
888
- auto info = torrent->info();
889
- if (!info) continue;
890
-
891
- // For now, we rely on torrent's add_peer which adds to pending_peers_
892
- // The connection is initiated here by calling network_manager_->connect_peer
893
- }
894
- }
895
-
896
- void BtClient::announce_torrents_to_trackers() {
897
- std::lock_guard<std::mutex> lock(mutex_);
898
-
899
- for (auto& [hash, torrent] : torrents_) {
900
- if (!torrent->is_active()) continue;
901
-
902
- // Skip metadata-only torrents (empty save_path means only fetching metadata)
903
- if (torrent->save_path().empty()) continue;
904
-
905
- auto tracker_it = tracker_managers_.find(hash);
906
- if (tracker_it == tracker_managers_.end()) continue;
907
-
908
- auto& tracker_mgr = tracker_it->second;
909
- if (!tracker_mgr->should_announce()) continue;
910
-
911
- auto stats = torrent->stats();
912
- auto info = torrent->info();
913
- if (!info) continue;
914
-
915
- TrackerRequest req;
916
- req.info_hash = hash;
917
- req.peer_id = peer_id_;
918
- req.port = network_manager_ ? network_manager_->listen_port() : config_.listen_port;
919
- req.uploaded = stats.total_uploaded;
920
- req.downloaded = stats.total_downloaded;
921
- req.left = stats.total_size - stats.bytes_done;
922
- req.event = TrackerEvent::NONE;
923
- req.numwant = 50;
924
-
925
- tracker_mgr->announce(req, [this, hash](
926
- const TrackerResponse& response, const std::string& tracker_url) {
927
-
928
- if (response.success && !response.peers.empty()) {
929
- LOG_INFO("BtClient", "Got " + std::to_string(response.peers.size()) +
930
- " peers from tracker");
931
-
932
- auto torrent = get_torrent(hash);
933
- if (torrent) {
934
- for (const auto& peer : response.peers) {
935
- torrent->add_peer(peer.ip, peer.port);
936
- }
937
- }
938
- }
939
- });
940
- }
941
- }
942
-
943
- //=============================================================================
944
- // Internal Methods
945
- //=============================================================================
946
-
947
- void BtClient::tick_loop() {
948
- LOG_INFO("BtClient", "Tick loop started");
949
-
950
- while (running_) {
951
- auto now = std::chrono::steady_clock::now();
952
-
953
- // Process deferred removals (outside main mutex to avoid deadlocks)
954
- {
955
- std::vector<BtInfoHash> to_remove;
956
- {
957
- std::lock_guard<std::mutex> lock(removal_mutex_);
958
- to_remove = std::move(pending_removals_);
959
- pending_removals_.clear();
960
- }
961
-
962
- for (const auto& hash : to_remove) {
963
- remove_torrent(hash);
964
- }
965
- }
966
-
967
- {
968
- std::lock_guard<std::mutex> lock(mutex_);
969
-
970
- // Tick all torrents
971
- for (auto& [hash, torrent] : torrents_) {
972
- torrent->tick();
973
- }
974
-
975
- // Connect pending peers (for both magnet and regular torrents)
976
- for (auto& [hash, torrent] : torrents_) {
977
- if (!torrent->is_active()) continue;
978
-
979
- auto pending = torrent->get_pending_peers();
980
- if (pending.empty()) continue;
981
-
982
- size_t connected = torrent->num_peers();
983
- size_t max_conn = config_.max_connections_per_torrent;
984
-
985
- // Connect to pending peers
986
- for (const auto& [ip, port] : pending) {
987
- if (connected >= max_conn) break;
988
-
989
- // For magnet links without metadata, num_pieces is 0
990
- uint32_t num_pieces = 0;
991
- if (torrent->has_metadata() && torrent->info()) {
992
- num_pieces = torrent->info()->num_pieces();
993
- }
994
-
995
- if (network_manager_->connect_peer(ip, port, hash, peer_id_, num_pieces)) {
996
- LOG_DEBUG("BtClient", "Connecting to peer " + ip + ":" + std::to_string(port));
997
- ++connected;
998
- }
999
- }
1000
-
1001
- // Clear pending peers after attempting connections
1002
- torrent->clear_pending_peers();
1003
- }
1004
- }
1005
-
1006
- // Periodic DHT announces (every 15 minutes)
1007
- // Skip metadata-only torrents (empty save_path) to avoid unnecessary network traffic
1008
- auto dht_elapsed = std::chrono::duration_cast<std::chrono::minutes>(
1009
- now - last_dht_announce_).count();
1010
- if (dht_elapsed >= 15 && dht_running_) {
1011
- std::lock_guard<std::mutex> lock(mutex_);
1012
- for (auto& [hash, torrent] : torrents_) {
1013
- // Skip metadata-only torrents (empty save_path means only fetching metadata)
1014
- if (torrent->is_active() && !torrent->save_path().empty()) {
1015
- announce_to_dht(hash);
1016
- }
1017
- }
1018
- last_dht_announce_ = now;
1019
- }
1020
-
1021
- // Periodic tracker announces (handled by tracker manager's should_announce)
1022
- auto tracker_elapsed = std::chrono::duration_cast<std::chrono::seconds>(
1023
- now - last_tracker_announce_).count();
1024
- if (tracker_elapsed >= 30) {
1025
- announce_torrents_to_trackers();
1026
- last_tracker_announce_ = now;
1027
- }
1028
-
1029
- // Sleep for a bit
1030
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
1031
- }
1032
-
1033
- LOG_INFO("BtClient", "Tick loop stopped");
1034
- }
1035
-
1036
- Torrent::Ptr BtClient::create_torrent(const TorrentInfo& info,
1037
- const std::string& save_path) {
1038
- TorrentConfig torrent_config;
1039
- torrent_config.save_path = save_path.empty() ? config_.download_path : save_path;
1040
- torrent_config.resume_data_path = config_.resume_data_path;
1041
- torrent_config.max_connections = config_.max_connections_per_torrent;
1042
- torrent_config.max_uploads = config_.max_uploads;
1043
-
1044
- return std::make_shared<Torrent>(info, torrent_config, peer_id_);
1045
- }
1046
-
1047
- } // namespace librats