librats 1.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,502 @@
1
+ #include "librats/bittorrent/peer_connection.h"
2
+ #include "librats/bittorrent/byte_io.h"
3
+ #include "librats/bittorrent/log.h"
4
+
5
+ #include <algorithm>
6
+ #include <cerrno>
7
+ #include <cstring>
8
+
9
+ namespace librats::bittorrent {
10
+
11
+ namespace {
12
+
13
+ #ifdef _WIN32
14
+ inline bool would_block() { return WSAGetLastError() == WSAEWOULDBLOCK; }
15
+ #else
16
+ inline bool would_block() { return errno == EAGAIN || errno == EWOULDBLOCK; }
17
+ #endif
18
+
19
+ /// Largest message we will accept. A bitfield for ~16M pieces fits; a piece
20
+ /// message is ~16 KiB. Anything larger is treated as a protocol violation.
21
+ constexpr std::uint32_t kMaxMessageLen = 2 * 1024 * 1024;
22
+
23
+ constexpr std::size_t kRecvChunk = 64 * 1024;
24
+
25
+ /// Ceiling on how far rx_ may be grown on the strength of a *declared* message length
26
+ /// alone. Sizing the buffer for the whole message up front (as libtorrent does, growing
27
+ /// straight to packet_size) turns a large message into one allocation instead of a
28
+ /// 1.5x-at-a-time climb. But the length is the peer's word, so past this we fall back
29
+ /// to geometric growth and the allocation only ever tracks bytes that really arrived.
30
+ /// Comfortably above anything real (a piece is 16 KiB + 9; the largest honest message
31
+ /// is a bitfield, ~1 byte per 8 pieces).
32
+ constexpr std::size_t kMaxEagerReserve = 1024 * 1024;
33
+
34
+ /// Send-buffer high-water mark. If a peer stops draining its socket and our
35
+ /// unsent backlog blows past this, the peer is a slow consumer — drop it rather
36
+ /// than buffer without bound. Matches librats' own kDefaultSendHighWater (8 MiB);
37
+ /// far above any single legitimate message (a piece is <=32 KiB, a bitfield for a
38
+ /// realistic torrent well under a MiB).
39
+ constexpr std::size_t kSendHighWater = 8 * 1024 * 1024;
40
+
41
+ // ---- connection timeouts (enforced by tick()) ----
42
+ /// Drop an incoming peer that hasn't completed the handshake in this long — stops
43
+ /// idle half-open sockets from accumulating (a cheap DoS).
44
+ constexpr auto kHandshakeTimeout = std::chrono::seconds(30);
45
+ /// Drop a connected peer that has sent us nothing for this long.
46
+ constexpr auto kIdleTimeout = std::chrono::seconds(120);
47
+ /// Send a keep-alive if we haven't sent anything for this long (kept below a
48
+ /// typical peer's ~120 s idle timeout so we don't get dropped).
49
+ constexpr auto kKeepAliveInterval = std::chrono::seconds(100);
50
+ /// How often tick() runs. Coarse: it only checks the deadlines above.
51
+ constexpr auto kTickInterval = std::chrono::seconds(10);
52
+
53
+ } // namespace
54
+
55
+ PeerConnection::PeerConnection(Reactor& reactor, socket_t sock, bool outgoing,
56
+ const InfoHash& info_hash, const PeerId& our_peer_id,
57
+ std::uint32_t num_pieces, Observer* observer,
58
+ std::string remote_ip, std::uint16_t remote_port)
59
+ : reactor_(reactor)
60
+ , sock_(sock)
61
+ , outgoing_(outgoing)
62
+ , info_hash_(info_hash)
63
+ , our_peer_id_(our_peer_id)
64
+ , num_pieces_(num_pieces)
65
+ , obs_(observer)
66
+ , bound_(true)
67
+ , remote_ip_(std::move(remote_ip))
68
+ , remote_port_(remote_port)
69
+ , peer_have_(num_pieces, false) {}
70
+
71
+ PeerConnection::PeerConnection(Reactor& reactor, socket_t sock, const PeerId& our_peer_id,
72
+ Resolver resolver, std::string remote_ip, std::uint16_t remote_port)
73
+ : reactor_(reactor)
74
+ , sock_(sock)
75
+ , outgoing_(false)
76
+ , info_hash_{}
77
+ , our_peer_id_(our_peer_id)
78
+ , num_pieces_(0)
79
+ , obs_(nullptr)
80
+ , resolver_(std::move(resolver))
81
+ , bound_(false)
82
+ , remote_ip_(std::move(remote_ip))
83
+ , remote_port_(remote_port) {}
84
+
85
+ PeerConnection::~PeerConnection() {
86
+ // Cancel the tick before we die so its captured `this` can never fire on freed
87
+ // memory. Same reactor thread owns both the timer and this destructor.
88
+ if (tick_timer_ != kInvalidTimerId) { reactor_.cancel(tick_timer_); tick_timer_ = kInvalidTimerId; }
89
+ if (!closed_ && is_valid_socket(sock_)) {
90
+ reactor_.remove(sock_);
91
+ close_socket(sock_);
92
+ sock_ = RATS_INVALID_SOCKET;
93
+ }
94
+ }
95
+
96
+ void PeerConnection::start() {
97
+ if (started_) return;
98
+ started_ = true;
99
+ set_socket_nonblocking(sock_);
100
+ reactor_.add(sock_, PollIn, [this](std::uint32_t ev) { on_io(ev); });
101
+
102
+ const auto now = std::chrono::steady_clock::now();
103
+ created_ = last_recv_ = last_sent_ = now;
104
+ tick_timer_ = reactor_.schedule(kTickInterval, [this] { tick(); });
105
+
106
+ // An outgoing peer sends its handshake immediately; an incoming one waits to
107
+ // learn the info-hash, then replies (see parse_handshake()).
108
+ if (outgoing_) {
109
+ LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " → handshake sent ("
110
+ << short_hash(info_hash_) << ')');
111
+ send_handshake();
112
+ }
113
+ }
114
+
115
+ void PeerConnection::send_handshake() {
116
+ std::uint8_t hs[kHandshakeSize];
117
+ hs[0] = std::uint8_t(kProtocolStringLen);
118
+ std::memcpy(hs + 1, kProtocolString, kProtocolStringLen);
119
+ ReservedBytes reserved{};
120
+ reserved::enable_dht(reserved);
121
+ // NOTE: we deliberately do NOT advertise the Fast Extension (BEP 6). We do not
122
+ // implement its messages (Have All / Have None / Suggest / Reject / Allowed
123
+ // Fast), and a peer that sees the Fast bit and also supports it would replace
124
+ // its initial `bitfield` with a `Have All` / `Have None` we'd silently ignore —
125
+ // making fast-capable seeds (i.e. most of the modern swarm) look like they hold
126
+ // nothing, so we'd never download from them. Re-enable only once BEP 6 is
127
+ // actually implemented in dispatch().
128
+ reserved::enable_extensions(reserved);
129
+ std::memcpy(hs + 20, reserved.data(), 8);
130
+ std::memcpy(hs + 28, info_hash_.data(), 20);
131
+ std::memcpy(hs + 48, our_peer_id_.data(), 20);
132
+ handshake_sent_ = true;
133
+ queue(ByteView(hs, kHandshakeSize));
134
+ flush();
135
+ }
136
+
137
+ void PeerConnection::close(const std::string& reason) {
138
+ if (closed_) return;
139
+ closed_ = true;
140
+ // Single choke point for every teardown path (protocol error, timeout, slow
141
+ // consumer, remote close, torrent stop): one greppable line per disconnect.
142
+ LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " disconnect: " << reason);
143
+ if (tick_timer_ != kInvalidTimerId) { reactor_.cancel(tick_timer_); tick_timer_ = kInvalidTimerId; }
144
+ if (is_valid_socket(sock_)) {
145
+ reactor_.remove(sock_);
146
+ close_socket(sock_);
147
+ sock_ = RATS_INVALID_SOCKET;
148
+ }
149
+ // Drop the send backlog; rx_ is deliberately left alone — close() can be called
150
+ // from inside a message handler that still holds a ByteView into it.
151
+ tx_.clear();
152
+ if (obs_) obs_->on_closed(*this, reason);
153
+ }
154
+
155
+ // ---- I/O ----
156
+
157
+ void PeerConnection::on_io(std::uint32_t events) {
158
+ if (closed_) return;
159
+ if (events & PollOut) flush();
160
+ if (closed_) return;
161
+ if (events & PollIn) do_read();
162
+ if (closed_) return;
163
+ if (events & (PollErr | PollHup)) close("socket error");
164
+ }
165
+
166
+ void PeerConnection::do_read() {
167
+ // Drain the kernel buffer, parsing after every read. Parsing inside the loop
168
+ // (rather than once the socket is empty) is what bounds rx_ to a single in-flight
169
+ // message — otherwise a seed pushing pieces at line rate would grow it to
170
+ // however much it managed to deliver before we caught up.
171
+ //
172
+ // The loop must run to would-block or to a short read (kernel buffer now empty):
173
+ // the kqueue backend is edge-triggered, so leaving unread data behind would stall
174
+ // the connection until the peer happens to send more.
175
+ for (;;) {
176
+ const ByteSpan into = rx_.prepare(read_size());
177
+
178
+ const int n = ::recv(sock_, reinterpret_cast<char*>(into.data()),
179
+ static_cast<int>(into.size()), 0);
180
+ if (n == 0) { close("peer closed connection"); return; }
181
+ if (n < 0) {
182
+ if (would_block()) return;
183
+ close("recv error");
184
+ return;
185
+ }
186
+
187
+ last_recv_ = std::chrono::steady_clock::now();
188
+ rx_.commit(std::size_t(n));
189
+
190
+ parse();
191
+ if (closed_) return;
192
+
193
+ if (std::size_t(n) < into.size()) return; // kernel buffer drained
194
+ }
195
+ }
196
+
197
+ std::size_t PeerConnection::read_size() const {
198
+ // Ask for the rest of the message we are mid-way through, so a big one (a bitfield,
199
+ // a metadata piece) lands in a single allocation rather than a series of 1.5x growth
200
+ // steps that memmove everything received so far at each one. Bounded by
201
+ // kMaxEagerReserve — rx_need_ is a length the *peer* declared.
202
+ //
203
+ // Note this asks for the remainder even when it is *smaller* than kRecvChunk. It is
204
+ // tempting to floor it at kRecvChunk ("read as much as we can anyway"), but
205
+ // prepare(n) is a demand for n bytes of free tail, and a buffer sized exactly for
206
+ // the message has no kRecvChunk of tail left near the end of it — so the floor would
207
+ // force one last 1.5x grow (memcpy'ing the whole message that already arrived) for
208
+ // room the message does not need. Nothing is lost by asking for less: prepare()
209
+ // hands back the *entire* free tail regardless, so the recv() is just as big.
210
+ if (rx_need_ > rx_.size()) return (std::min)(rx_need_ - rx_.size(), kMaxEagerReserve);
211
+ return kRecvChunk;
212
+ }
213
+
214
+ void PeerConnection::parse() {
215
+ rx_need_ = 0; // recomputed below: 0 == no message is mid-flight
216
+
217
+ if (!handshake_received_) {
218
+ if (rx_.size() < kHandshakeSize) { rx_need_ = kHandshakeSize; return; }
219
+ if (!parse_handshake()) return; // consumed 68 bytes (or closed)
220
+ }
221
+
222
+ while (!closed_ && rx_.size() >= 4) {
223
+ const std::uint32_t len = read_u32_be(rx_.data());
224
+ if (len > kMaxMessageLen) { close("oversize message"); return; }
225
+ if (rx_.size() < std::size_t(4) + len) {
226
+ rx_need_ = std::size_t(4) + len; // size rx_ for the whole message
227
+ break;
228
+ }
229
+ if (len == 0) { rx_.consume(4); continue; } // keep-alive
230
+
231
+ const std::uint8_t id = rx_.data()[4];
232
+ const std::uint8_t* payload = rx_.data() + 5;
233
+ const std::uint32_t plen = len - 1;
234
+ dispatch(MessageId(id), payload, plen);
235
+ if (closed_) return;
236
+ rx_.consume(std::size_t(4) + len);
237
+ }
238
+ }
239
+
240
+ bool PeerConnection::parse_handshake() {
241
+ const std::uint8_t* d = rx_.data();
242
+ if (d[0] != kProtocolStringLen || std::memcmp(d + 1, kProtocolString, kProtocolStringLen) != 0) {
243
+ close("bad protocol header");
244
+ return false;
245
+ }
246
+ std::memcpy(peer_reserved_.data(), d + 20, 8);
247
+
248
+ InfoHash their_info{};
249
+ std::memcpy(their_info.data(), d + 28, 20);
250
+
251
+ if (bound_) {
252
+ // Outgoing: the info-hash must be the one we dialed for.
253
+ if (their_info != info_hash_) { close("info-hash mismatch"); return false; }
254
+ } else {
255
+ // Incoming: resolve which torrent this is for and late-bind to it.
256
+ Binding b;
257
+ if (!resolver_ || !resolver_(their_info, b) || !b.observer) {
258
+ close("unknown torrent");
259
+ return false;
260
+ }
261
+ info_hash_ = their_info;
262
+ obs_ = b.observer;
263
+ num_pieces_ = b.num_pieces;
264
+ peer_have_ = Bitfield(num_pieces_, false);
265
+ bound_ = true;
266
+ }
267
+
268
+ std::memcpy(peer_id_.data(), d + 48, 20);
269
+ rx_.consume(kHandshakeSize);
270
+ handshake_received_ = true;
271
+
272
+ // An incoming connection now knows which torrent it is for and replies.
273
+ if (!outgoing_ && !handshake_sent_) send_handshake();
274
+
275
+ LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " ← handshake "
276
+ << identify_client(peer_id_) << " (" << short_hash(info_hash_) << ')');
277
+ if (obs_) obs_->on_handshake(*this, info_hash_, peer_id_);
278
+ return true;
279
+ }
280
+
281
+ void PeerConnection::dispatch(MessageId id, const std::uint8_t* payload, std::uint32_t len) {
282
+ auto bad = [&] { close("malformed message"); };
283
+
284
+ switch (id) {
285
+ case MessageId::Choke:
286
+ if (len != 0) return bad();
287
+ peer_choking_ = true;
288
+ if (obs_) obs_->on_choke(*this, true);
289
+ break;
290
+ case MessageId::Unchoke:
291
+ if (len != 0) return bad();
292
+ peer_choking_ = false;
293
+ if (obs_) obs_->on_choke(*this, false);
294
+ break;
295
+ case MessageId::Interested:
296
+ if (len != 0) return bad();
297
+ peer_interested_ = true;
298
+ if (obs_) obs_->on_interest(*this, true);
299
+ break;
300
+ case MessageId::NotInterested:
301
+ if (len != 0) return bad();
302
+ peer_interested_ = false;
303
+ if (obs_) obs_->on_interest(*this, false);
304
+ break;
305
+ case MessageId::Have: {
306
+ if (len != 4) return bad();
307
+ piece_state_begun_ = true; // a HAVE begins the piece-state flow; a later bitfield is invalid
308
+ const std::uint32_t piece = read_u32_be(payload);
309
+ // Only act on a HAVE that actually flips a bit. A redundant HAVE (a piece
310
+ // the peer already advertised, via bitfield or an earlier HAVE) is legal on
311
+ // the wire, but re-notifying the observer would inc availability a second
312
+ // time in the picker while disconnect only decrements once per set bit —
313
+ // leaving the count permanently skewed and corrupting rarest-first. An
314
+ // out-of-range index simply flips nothing and is ignored.
315
+ if (piece < peer_have_.size() && !peer_have_.get(piece)) {
316
+ peer_have_.set(piece);
317
+ if (obs_) obs_->on_have(*this, piece);
318
+ }
319
+ break;
320
+ }
321
+ case MessageId::Bitfield: {
322
+ // BEP 3: a bitfield is valid only as the peer's first piece-state
323
+ // message. A second bitfield (or one after any HAVE) would re-add the
324
+ // peer's whole availability in the picker with no matching decrement —
325
+ // a permanent skew. And when we know the piece count, the payload must
326
+ // be exactly ceil(num_pieces/8) bytes. Reject either as a violation.
327
+ if (piece_state_begun_) return bad();
328
+ if (num_pieces_ != 0 && len != (num_pieces_ + 7) / 8) return bad();
329
+ piece_state_begun_ = true;
330
+ const std::uint32_t bits = num_pieces_ ? num_pieces_ : len * 8;
331
+ peer_have_.assign(payload, len, bits);
332
+ if (obs_) obs_->on_bitfield(*this, peer_have_);
333
+ break;
334
+ }
335
+ case MessageId::Request: {
336
+ if (len != 12) return bad();
337
+ if (obs_) obs_->on_request(*this, read_u32_be(payload), read_u32_be(payload + 4),
338
+ read_u32_be(payload + 8));
339
+ break;
340
+ }
341
+ case MessageId::Piece: {
342
+ if (len < 8) return bad();
343
+ if (obs_) obs_->on_piece(*this, read_u32_be(payload), read_u32_be(payload + 4),
344
+ ByteView(payload + 8, len - 8));
345
+ break;
346
+ }
347
+ case MessageId::Cancel: {
348
+ if (len != 12) return bad();
349
+ if (obs_) obs_->on_cancel(*this, read_u32_be(payload), read_u32_be(payload + 4),
350
+ read_u32_be(payload + 8));
351
+ break;
352
+ }
353
+ case MessageId::Port:
354
+ if (len != 2) return bad();
355
+ if (obs_) obs_->on_port(*this, read_u16_be(payload));
356
+ break;
357
+ case MessageId::Extended:
358
+ if (len < 1) return bad();
359
+ if (obs_) obs_->on_extended(*this, payload[0], ByteView(payload + 1, len - 1));
360
+ break;
361
+ default:
362
+ break; // unknown id — ignore for forward compatibility
363
+ }
364
+ }
365
+
366
+ // ---- send ----
367
+
368
+ void PeerConnection::send_message(MessageId id, const std::uint8_t* payload, std::uint32_t len) {
369
+ std::uint8_t header[5];
370
+ write_u32_be(header, len + 1);
371
+ header[4] = std::uint8_t(id);
372
+ queue(ByteView(header, 5));
373
+ if (len) queue(ByteView(payload, len));
374
+ flush();
375
+ }
376
+
377
+ void PeerConnection::send_keepalive() {
378
+ const std::uint8_t z[4] = {0, 0, 0, 0};
379
+ queue(ByteView(z, 4));
380
+ flush();
381
+ }
382
+ void PeerConnection::send_choke() { am_choking_ = true; send_message(MessageId::Choke, nullptr, 0); }
383
+ void PeerConnection::send_unchoke() { am_choking_ = false; send_message(MessageId::Unchoke, nullptr, 0); }
384
+ void PeerConnection::send_interested() { am_interested_ = true; send_message(MessageId::Interested, nullptr, 0); }
385
+ void PeerConnection::send_not_interested(){ am_interested_ = false;send_message(MessageId::NotInterested, nullptr, 0); }
386
+
387
+ void PeerConnection::send_have(std::uint32_t piece) {
388
+ std::uint8_t p[4];
389
+ write_u32_be(p, piece);
390
+ send_message(MessageId::Have, p, 4);
391
+ }
392
+
393
+ void PeerConnection::send_bitfield(const Bitfield& bitfield) {
394
+ send_message(MessageId::Bitfield, bitfield.data(), std::uint32_t(bitfield.data_size()));
395
+ }
396
+
397
+ void PeerConnection::send_request(std::uint32_t piece, std::uint32_t offset, std::uint32_t length) {
398
+ std::uint8_t p[12];
399
+ write_u32_be(p, piece);
400
+ write_u32_be(p + 4, offset);
401
+ write_u32_be(p + 8, length);
402
+ send_message(MessageId::Request, p, 12);
403
+ }
404
+
405
+ void PeerConnection::send_piece(std::uint32_t piece, std::uint32_t offset, Bytes data) {
406
+ // The 13-byte header packs into the send queue's scratch chunk; the block is moved
407
+ // in as its own chunk, so the buffer the disk read filled is never copied. Gather
408
+ // I/O reunites the two, so this is still one send().
409
+ std::uint8_t header[13];
410
+ write_u32_be(header, std::uint32_t(9 + data.size()));
411
+ header[4] = std::uint8_t(MessageId::Piece);
412
+ write_u32_be(header + 5, piece);
413
+ write_u32_be(header + 9, offset);
414
+ queue(ByteView(header, sizeof(header)));
415
+ if (!data.empty()) queue(std::move(data));
416
+ flush();
417
+ }
418
+
419
+ void PeerConnection::send_cancel(std::uint32_t piece, std::uint32_t offset, std::uint32_t length) {
420
+ std::uint8_t p[12];
421
+ write_u32_be(p, piece);
422
+ write_u32_be(p + 4, offset);
423
+ write_u32_be(p + 8, length);
424
+ send_message(MessageId::Cancel, p, 12);
425
+ }
426
+
427
+ void PeerConnection::send_port(std::uint16_t port) {
428
+ std::uint8_t p[2];
429
+ write_u16_be(p, port);
430
+ send_message(MessageId::Port, p, 2);
431
+ }
432
+
433
+ void PeerConnection::send_extended(std::uint8_t ext_id, ByteView payload) {
434
+ std::uint8_t header[6];
435
+ write_u32_be(header, std::uint32_t(2 + payload.size()));
436
+ header[4] = std::uint8_t(MessageId::Extended);
437
+ header[5] = ext_id;
438
+ queue(ByteView(header, sizeof(header)));
439
+ if (!payload.empty()) queue(payload);
440
+ flush();
441
+ }
442
+
443
+ void PeerConnection::flush() {
444
+ if (closed_) return;
445
+
446
+ while (!tx_.empty()) {
447
+ // One syscall for the whole backlog, however many chunks it spans.
448
+ ByteView slices[kMaxSendSlices];
449
+ const std::size_t count = tx_.gather(slices, kMaxSendSlices);
450
+
451
+ const std::ptrdiff_t n = send_vectored(sock_, slices, count);
452
+ if (n > 0) {
453
+ last_sent_ = std::chrono::steady_clock::now();
454
+ tx_.pop_front(std::size_t(n));
455
+ continue;
456
+ }
457
+ if (n == 0) break; // nothing accepted; retry on PollOut
458
+ if (would_block()) break; // congested; the backlog waits for PollOut
459
+ close("send error");
460
+ return;
461
+ }
462
+
463
+ want_write(!tx_.empty());
464
+
465
+ // Backpressure: allocated() is the memory the queue actually holds (a partially
466
+ // sent chunk keeps its whole allocation). Past the high-water mark the peer isn't
467
+ // draining us — drop it rather than buffer without bound.
468
+ if (tx_.allocated() > kSendHighWater) close("slow consumer: send buffer overflow");
469
+ }
470
+
471
+ void PeerConnection::want_write(bool on) {
472
+ if (on == want_write_ || closed_) return;
473
+ want_write_ = on;
474
+ reactor_.modify(sock_, PollIn | (on ? PollOut : PollNone));
475
+ }
476
+
477
+ void PeerConnection::tick() {
478
+ if (closed_) return;
479
+ const auto now = std::chrono::steady_clock::now();
480
+
481
+ if (!handshake_done()) {
482
+ // A peer that connects but never completes the handshake is dropped so it
483
+ // can't occupy a socket indefinitely.
484
+ if (now - created_ > kHandshakeTimeout) { close("handshake timeout"); return; }
485
+ } else {
486
+ if (now - last_recv_ > kIdleTimeout) { close("idle timeout"); return; }
487
+ // Keep the link alive if we've been quiet, so the peer doesn't drop us.
488
+ if (now - last_sent_ > kKeepAliveInterval) send_keepalive(); // updates last_sent_ via flush
489
+ }
490
+
491
+ if (closed_) return; // send_keepalive() may have hit a send error and closed us
492
+
493
+ // Hand back the receive buffer of a peer that has gone quiet. Without this a peer
494
+ // that sent one big message (a bitfield, a metadata piece) and then went idle would
495
+ // sit on that allocation for as long as the connection lives — the traffic-driven
496
+ // shrink in ReceiveBuffer only ever samples when a message actually arrives.
497
+ rx_.decay();
498
+
499
+ tick_timer_ = reactor_.schedule(kTickInterval, [this] { tick(); });
500
+ }
501
+
502
+ } // namespace librats::bittorrent