librats 1.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -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