feat(torad): upgrade librqbit to 9.0.0-rc.0 and add --bind-device

Pinned exactly (=9.0.0-rc.0) — no v9 stable exists yet, so a caret range
would silently drift onto a future prerelease.

Two source changes were needed for the upgrade:

- `torrent_from_bytes_ext` moved to `librqbit_core::torrent_metainfo::
  torrent_from_bytes` and no longer wraps the result in a `meta` field.
- `peer_stats.live` / `.not_needed` are now `u32`, so the casts are
  redundant.

v9 also adds `SessionOptions::bind_device_name`, which is the reason for
the upgrade: it applies SO_BINDTODEVICE to every librqbit socket — peer
connections, trackers, DHT and LSD. Binding those to a VPN interface
means that when the interface goes away the sockets error out instead of
falling back to the host route, giving a kernel-enforced kill switch.
Exposed as --bind-device / TORAD_BIND_DEVICE; unset reproduces today's
behaviour exactly.

Note that `listen` stays at its default of None, so there is no listener
and no uTP socket in either direction — torad is TCP-only and leech-only.
That is unchanged from v8 but now written down, since incoming
connections need NAT-PMP port forwarding that we have not built.

Also drops torad's `nix` pin from 0.29 to 0.31.3 to match the sibling
tora crate; the workspace was carrying three copies.

Verified end-to-end against a real swarm rather than by compiling alone:
a 755 MiB torrent added via HTTP .torrent URL, driven through
pending -> downloading -> finished, with pause/resume on an active
torrent, remove, and the notification stream all exercised. Evidence in
.omo/evidence/task-5-torad-vpn-namespace.txt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Alexander
2026-08-15 00:17:09 +02:00
parent 7842b5aeba
commit dd41f1961f
5 changed files with 384 additions and 100 deletions
Generated
+342 -87
View File
@@ -97,6 +97,12 @@ dependencies = [
"rustversion",
]
[[package]]
name = "arrayvec"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "assert_cfg"
version = "0.1.0"
@@ -208,6 +214,8 @@ dependencies = [
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
@@ -216,9 +224,11 @@ dependencies = [
"pin-project-lite",
"serde_core",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -237,17 +247,43 @@ dependencies = [
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backoff"
version = "0.4.0"
name = "axum-extra"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96"
dependencies = [
"getrandom 0.2.17",
"instant",
"rand 0.8.6",
"axum",
"axum-core",
"bytes",
"form_urlencoded",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"serde_core",
"serde_html_form",
"serde_path_to_error",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backon"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
dependencies = [
"fastrand",
"gloo-timers",
"tokio",
]
[[package]]
@@ -256,15 +292,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bincode"
version = "2.0.1"
@@ -397,6 +424,17 @@ dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "chardetng"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
dependencies = [
"cfg-if",
"encoding_rs",
"memchr",
]
[[package]]
name = "chrono"
version = "0.4.45"
@@ -724,6 +762,18 @@ dependencies = [
"syn",
]
[[package]]
name = "dontfrag"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117949f5a8b25ba471b4dfea927a07a2f8730c585532a2eb4294e18f5155397"
dependencies = [
"cfg-if",
"libc",
"tokio",
"windows-sys 0.52.0",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -751,6 +801,15 @@ dependencies = [
"serde",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -788,6 +847,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -1011,6 +1076,18 @@ dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "gloo-timers"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "governor"
version = "0.10.4"
@@ -1405,15 +1482,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "intervaltree"
version = "0.2.7"
@@ -1505,18 +1573,19 @@ dependencies = [
[[package]]
name = "librqbit"
version = "8.1.1"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dadca8f521242010a4c846ef5f224c217009c92e272709cdc08ba9cdabe62983"
checksum = "b9451def41c18d1b8e7b68c097bcb0c9a0579cdc9b779803368a65b86cae03d2"
dependencies = [
"anyhow",
"arc-swap",
"async-compression",
"async-stream",
"async-trait",
"backoff",
"axum-extra",
"backon",
"base64",
"bincode 2.0.1",
"bincode",
"bitvec",
"byteorder",
"bytes",
@@ -1532,22 +1601,29 @@ dependencies = [
"librqbit-clone-to-owned",
"librqbit-core",
"librqbit-dht",
"librqbit-dualstack-sockets",
"librqbit-lsd",
"librqbit-peer-protocol",
"librqbit-sha1-wrapper",
"librqbit-tracker-comms",
"librqbit-upnp",
"librqbit-utp",
"memmap2",
"mime_guess",
"nix 0.30.1",
"parking_lot",
"rand 0.9.4",
"regex",
"reqwest",
"rlimit",
"serde",
"serde_derive",
"serde_json",
"serde_urlencoded",
"serde_with",
"size_format",
"socket2",
"thiserror 2.0.18",
"tokio",
"tokio-socks",
"tokio-stream",
@@ -1557,61 +1633,71 @@ dependencies = [
"urlencoding",
"uuid",
"walkdir",
"windows",
]
[[package]]
name = "librqbit-bencode"
version = "3.1.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "606dff526ba81e3eca33e2bb28b53afa2bc0b2c41d252333fa44e6c11abb37da"
checksum = "97490fb9b35fb575f498cc1e804b23be6bbf3603d5a2358733e5b867d1ee52e8"
dependencies = [
"anyhow",
"arrayvec",
"atoi",
"bytes",
"librqbit-buffers",
"librqbit-clone-to-owned",
"librqbit-sha1-wrapper",
"serde",
"serde_derive",
"thiserror 2.0.18",
]
[[package]]
name = "librqbit-buffers"
version = "4.2.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c78b907d6171a7191c162b2b60db46d254ebde6a95282b77372af556c1463"
checksum = "e845dbd14174712471c3430c958d5c25e391561c2dd31f39f895bb60f63c46f0"
dependencies = [
"bytes",
"librqbit-clone-to-owned",
"serde",
"serde_derive",
]
[[package]]
name = "librqbit-clone-to-owned"
version = "3.0.1"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd1e66d773ba9c475ff89286dc1d6f9d167cbb898603797467dd0ea6844c445"
checksum = "76ef698eb8d7745f425f2b9a93e6a9d946bcb8bf84a559303e0fc3f01e8ad07c"
dependencies = [
"bytes",
]
[[package]]
name = "librqbit-core"
version = "5.0.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55a02cc6fce6743ad38661ccd6fafc6cf1ae5e0106a9922836b0524dbe752378"
checksum = "a38e805aad201d96024849649174015d48a575348b35bbd927d99b47464e665e"
dependencies = [
"anyhow",
"assert_cfg",
"bytes",
"chardetng",
"data-encoding",
"directories",
"encoding_rs",
"hex",
"itertools",
"librqbit-bencode",
"librqbit-buffers",
"librqbit-clone-to-owned",
"librqbit-sha1-wrapper",
"memchr",
"parking_lot",
"rand 0.9.4",
"serde",
"serde_derive",
"thiserror 2.0.18",
"tokio",
"tokio-util",
"tracing",
@@ -1620,27 +1706,29 @@ dependencies = [
[[package]]
name = "librqbit-dht"
version = "5.3.1"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cc129194337771a86b0399956c4d9bf1cd97c5f24d14a50be38e170f76a54b"
checksum = "d59a9374b33a52a065171641ae518db97969d3db849a57824c72c53d2109918b"
dependencies = [
"anyhow",
"backoff",
"byteorder",
"backon",
"bytes",
"chrono",
"dashmap",
"futures",
"hex",
"indexmap 2.14.0",
"leaky-bucket",
"librqbit-bencode",
"librqbit-buffers",
"librqbit-clone-to-owned",
"librqbit-core",
"librqbit-dualstack-sockets",
"parking_lot",
"rand 0.9.4",
"serde",
"serde_derive",
"serde_json",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tokio-util",
@@ -1648,13 +1736,49 @@ dependencies = [
]
[[package]]
name = "librqbit-peer-protocol"
version = "4.3.0"
name = "librqbit-dualstack-sockets"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a73129497b500505f33d1dc0426319b6a6a208f13fdfaae56224ab8c2346a773"
checksum = "f7189f1ff66ca75055137a38b062ba7df9691d071b7172e5090997c62eb4b4de"
dependencies = [
"axum",
"backon",
"futures",
"libc",
"network-interface",
"socket2",
"thiserror 2.0.18",
"tokio",
"tracing",
]
[[package]]
name = "librqbit-lsd"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e65ddc3b64392a71378944712c9830aac8ef98f860b180419dd42b4596e7faa"
dependencies = [
"anyhow",
"atoi",
"bstr",
"futures",
"httparse",
"librqbit-core",
"librqbit-dualstack-sockets",
"parking_lot",
"rand 0.9.4",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "librqbit-peer-protocol"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ade1bbea8cf3e7eb51978b32a549787d17bbf872f3f0ed47d7c85e7377705a"
dependencies = [
"anyhow",
"bincode 1.3.3",
"bitvec",
"byteorder",
"bytes",
@@ -1664,13 +1788,16 @@ dependencies = [
"librqbit-clone-to-owned",
"librqbit-core",
"serde",
"serde_derive",
"thiserror 2.0.18",
"tracing",
]
[[package]]
name = "librqbit-sha1-wrapper"
version = "4.1.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79373a02db73159e4de7ca5d27b6eeae2d540df66c6801db2b01c5513d087524"
checksum = "eeb119e5c05f1aa4011cf2e4dc19f83403064d2210804e2f35f06abac4e5b710"
dependencies = [
"assert_cfg",
"aws-lc-rs",
@@ -1678,21 +1805,26 @@ dependencies = [
[[package]]
name = "librqbit-tracker-comms"
version = "3.0.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08204944c5be677a5de8e1230e0249fce5c14abef23048e26452c6fb03f1b260"
checksum = "0ae0c520cc5f028fb50d746eaa6ba2aa082228db96c2baf1a312f9cacfceeb58"
dependencies = [
"anyhow",
"async-stream",
"backon",
"byteorder",
"futures",
"itertools",
"librqbit-bencode",
"librqbit-buffers",
"librqbit-core",
"librqbit-dualstack-sockets",
"parking_lot",
"rand 0.9.4",
"reqwest",
"serde",
"serde_derive",
"serde_with",
"tokio",
"tokio-util",
"tracing",
@@ -1702,23 +1834,49 @@ dependencies = [
[[package]]
name = "librqbit-upnp"
version = "1.0.0"
version = "9.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545aad6124c97201055983137e12a19f34acad565120c3cd30596cbd72e8fa86"
checksum = "058af0d5e2a99c567b73369455e3852c48addcf751836f08cfc25ca89dd16cc3"
dependencies = [
"anyhow",
"bstr",
"futures",
"httparse",
"librqbit-dualstack-sockets",
"network-interface",
"quick-xml",
"reqwest",
"serde",
"serde_derive",
"socket2",
"tokio",
"tracing",
"url",
]
[[package]]
name = "librqbit-utp"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3bfdc73944bc76cab24d5690a98816770040a654c449edf5ff2b9ba22626aa"
dependencies = [
"bitvec",
"dontfrag",
"lazy_static",
"libc",
"librqbit-dualstack-sockets",
"metrics",
"parking_lot",
"rand 0.9.4",
"ringbuf",
"rustc-hash",
"socket2",
"thiserror 2.0.18",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "libsqlite3-sys"
version = "0.37.0"
@@ -1787,6 +1945,16 @@ dependencies = [
"libc",
]
[[package]]
name = "metrics"
version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89550ee9f79e88fef3119de263694973a8adb26c21d75322164fb8c493039fe2"
dependencies = [
"portable-atomic",
"rapidhash",
]
[[package]]
name = "mime"
version = "0.3.17"
@@ -1836,6 +2004,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nix"
version = "0.31.3"
@@ -2033,6 +2213,15 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]]
name = "potential_utf"
version = "0.1.5"
@@ -2106,9 +2295,9 @@ dependencies = [
[[package]]
name = "quick-xml"
version = "0.37.5"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
"serde",
@@ -2196,24 +2385,13 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha 0.9.0",
"rand_chacha",
"rand_core 0.9.5",
]
@@ -2228,16 +2406,6 @@ dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
@@ -2248,15 +2416,6 @@ dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "rand_core"
version = "0.9.5"
@@ -2272,6 +2431,15 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rapidhash"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e"
dependencies = [
"rustversion",
]
[[package]]
name = "raw-cpuid"
version = "11.6.0"
@@ -2405,6 +2573,17 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "ringbuf"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c"
dependencies = [
"crossbeam-utils",
"portable-atomic",
"portable-atomic-util",
]
[[package]]
name = "rlimit"
version = "0.10.2"
@@ -2536,6 +2715,19 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_html_form"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"
dependencies = [
"form_urlencoded",
"indexmap 2.14.0",
"itoa",
"ryu",
"serde_core",
]
[[package]]
name = "serde_json"
version = "1.0.150"
@@ -2549,6 +2741,17 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [
"itoa",
"serde",
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -3204,7 +3407,7 @@ dependencies = [
"clap",
"colored",
"hyper-util",
"nix",
"nix 0.31.3",
"serde_json",
"tokio",
"tokio-stream",
@@ -3234,6 +3437,7 @@ dependencies = [
"clap",
"librqbit",
"librqbit-core",
"nix 0.31.3",
"reqwest",
"sha1 0.10.7",
"sqlx",
@@ -3657,6 +3861,27 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections",
"windows-core",
"windows-future",
"windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core",
]
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -3670,6 +3895,17 @@ dependencies = [
"windows-strings",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core",
"windows-link",
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
@@ -3698,6 +3934,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.4.1"
@@ -3750,6 +3996,15 @@ dependencies = [
"windows_x86_64_msvc",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
dependencies = [
"windows-link",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
+3 -2
View File
@@ -7,8 +7,9 @@ edition = "2024"
anyhow = "1.0.103"
async-trait = "0.1"
clap = { version = "4.6.1", features = ["derive", "env"] }
librqbit = { version = "8.1.1", default-features = false, features = ["rust-tls", "http-api-client"] }
librqbit-core = { version = "5.0.0", default-features = false }
librqbit = { version = "=9.0.0-rc.0", default-features = false, features = ["rust-tls", "http-api-client"] }
librqbit-core = { version = "=9.0.0-rc.0", default-features = false }
nix = { version = "0.31.3", features = ["user", "sched", "fs", "process"] }
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
sha1 = "0.10"
sqlx = { version = "0.9.0", features = ["postgres", "runtime-tokio", "tls-rustls", "uuid"] }
+7 -1
View File
@@ -39,6 +39,12 @@ struct Args {
/// (e.g. `kill $(cat /tmp/torad.pid)`).
#[arg(long, env = "TORAD_PID_FILE")]
pid_file: Option<PathBuf>,
/// Network device to bind torrent sockets to (Linux: SO_BINDTODEVICE).
/// When set, all BT peer, tracker, and DHT traffic egresses only through
/// this interface. When unset, librqbit uses default routing.
#[arg(long, env = "TORAD_BIND_DEVICE")]
bind_device: Option<String>,
}
fn default_socket_path() -> PathBuf {
@@ -70,7 +76,7 @@ async fn main() -> Result<()> {
.context("failed to connect to Postgres")?;
info!("connected to Postgres");
let manager = TorrentManager::new(pool.clone(), download_dir)
let manager = TorrentManager::new(pool.clone(), download_dir, args.bind_device)
.await
.context("failed to start torrent manager")?;
manager.clone().spawn_poller();
+3 -3
View File
@@ -13,14 +13,14 @@
//! parsing it) yields the correct hash even for spec-violating files.
use anyhow::{Context, Result};
use librqbit::torrent_from_bytes_ext;
use librqbit_core::torrent_metainfo::torrent_from_bytes;
use sha1::{Digest, Sha1};
/// Extracts the v1 info-hash from `.torrent` bytes.
pub(super) fn from_bytes(bytes: &[u8]) -> Result<String> {
// Fast path: spec-compliant torrent, librqbit's parser handles it.
if let Ok(parsed) = torrent_from_bytes_ext::<Vec<u8>>(bytes) {
return Ok(parsed.meta.info_hash.as_string());
if let Ok(parsed) = torrent_from_bytes(bytes) {
return Ok(parsed.info_hash.as_string());
}
// Fallback: locate the `info` value's byte span and SHA1 it directly.
let span = locate_info_value(bytes)
+29 -7
View File
@@ -5,7 +5,9 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{Context, Result};
use librqbit::api::TorrentIdOrHash;
use librqbit::{AddTorrent, AddTorrentOptions, ManagedTorrent, Session, TorrentStatsState};
use librqbit::{
AddTorrent, AddTorrentOptions, ManagedTorrent, Session, SessionOptions, TorrentStatsState,
};
use librqbit_core::Id20;
use sqlx::PgPool;
use tokio::sync::{Mutex, broadcast};
@@ -52,16 +54,36 @@ pub struct TorrentManager {
}
impl TorrentManager {
pub async fn new(pool: PgPool, download_dir: PathBuf) -> Result<Arc<Self>> {
pub async fn new(
pool: PgPool,
download_dir: PathBuf,
bind_device: Option<String>,
) -> Result<Arc<Self>> {
std::fs::create_dir_all(&download_dir).with_context(|| {
format!(
"failed to create download directory {}",
download_dir.display()
)
})?;
let session = Session::new(download_dir.clone())
.await
.context("failed to create librqbit session")?;
// `bind_device` scopes every librqbit socket — peers, trackers, DHT, LSD —
// to one interface via SO_BINDTODEVICE. When the interface goes away those
// sockets error instead of falling back to the host route, which is the
// kill switch VPN mode relies on. `None` reproduces the default behaviour:
// `Session::new` is itself `new_with_opts(dir, SessionOptions::default())`.
//
// `listen` stays at its default of `None`, so there is no listener and no
// uTP socket in either direction — torad is TCP-only and leech-only. That
// is a deliberate limitation for now; incoming connections need NAT-PMP
// port forwarding, which is deferred.
let session = Session::new_with_opts(
download_dir.clone(),
SessionOptions {
bind_device_name: bind_device,
..Default::default()
},
)
.await
.context("failed to create librqbit session")?;
let source = SourceResolver::new().context("failed to build source resolver")?;
let (events, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
Ok(Arc::new(Self {
@@ -654,8 +676,8 @@ fn live_stats_from_stats(stats: &librqbit::TorrentStats) -> LiveTorrentStats {
eta_seconds: eta,
uploaded_bytes,
upload_speed_bps: upload_bps,
peers: live.snapshot.peer_stats.live as u32,
seeds: live.snapshot.peer_stats.not_needed as u32,
peers: live.snapshot.peer_stats.live,
seeds: live.snapshot.peer_stats.not_needed,
}
}