From d5d2c0d497058d726c87925ff2925f8bc41aaa86 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Wed, 15 Apr 2026 05:54:19 -0400 Subject: [PATCH 1/2] runtime, derp: quiet noisy logs Signed-off-by: Nathan Perry Change-Id: I464cf0501b290530079a858484d521ae6a6a6964 --- ts_runtime/src/multiderp.rs | 2 +- ts_transport_derp/src/async_tokio.rs | 2 +- ts_transport_derp/src/dial.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ts_runtime/src/multiderp.rs b/ts_runtime/src/multiderp.rs index f7551247..c0217231 100644 --- a/ts_runtime/src/multiderp.rs +++ b/ts_runtime/src/multiderp.rs @@ -121,7 +121,7 @@ impl Multiderp { } } -#[tracing::instrument(skip_all, fields(region_id = %id), name = "derp packet transport")] +#[tracing::instrument(skip_all, fields(region_id = %id), name = "derp runner")] async fn run_derp_once( id: RegionId, region: &DerpRegion, diff --git a/ts_transport_derp/src/async_tokio.rs b/ts_transport_derp/src/async_tokio.rs index f0570271..67275a93 100644 --- a/ts_transport_derp/src/async_tokio.rs +++ b/ts_transport_derp/src/async_tokio.rs @@ -165,7 +165,7 @@ where FrameType::KeepAlive => { // TODO (dylan): do we need to do anything on KeepAlive other than reset a timer? // TODO (dylan): handle KeepAlive timer - tracing::debug!(transport = %self, "received KeepAlive frame"); + tracing::trace!("received KeepAlive frame"); } FrameType::Ping => { let Some((&ping, _)) = frame.as_type::() else { diff --git a/ts_transport_derp/src/dial.rs b/ts_transport_derp/src/dial.rs index 7744cbb1..94ff2f8d 100644 --- a/ts_transport_derp/src/dial.rs +++ b/ts_transport_derp/src/dial.rs @@ -121,7 +121,7 @@ pub async fn dial_region_tcp<'c>( match dial_server(server).await { Ok(Some(conn)) => { - tracing::debug!( + tracing::trace!( remote_addr = %conn.peer_addr().unwrap_or((core::net::Ipv4Addr::UNSPECIFIED, 0).into()), %server.hostname, "derp tcp dial ok", From b99f91a3d975c33ff10cc2b4efe9a50dcfc4a39e Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Wed, 15 Apr 2026 05:54:19 -0400 Subject: [PATCH 2/2] tailscale: ssh helpers and example Provide a happy-path integration with `russh` and `ratatui` that makes it easier to build in-process SSH TUIs with tailscale-rs. Signed-off-by: Nathan Perry Change-Id: I1c49b666d92dcc76076e35585ae4fe7e6a6a6964 --- Cargo.lock | 2075 ++++++++++++++++++++++++++-- Cargo.toml | 13 +- examples/ssh_peer_lookup/README.md | 22 + examples/ssh_peer_lookup/main.rs | 234 ++++ flake.nix | 17 + src/lib.rs | 2 + src/ssh/channel_server.rs | 291 ++++ src/ssh/channel_write.rs | 46 + src/ssh/mod.rs | 112 ++ src/ssh/ratatui.rs | 160 +++ 10 files changed, 2828 insertions(+), 144 deletions(-) create mode 100644 examples/ssh_peer_lookup/README.md create mode 100644 examples/ssh_peer_lookup/main.rs create mode 100644 src/ssh/channel_server.rs create mode 100644 src/ssh/channel_write.rs create mode 100644 src/ssh/mod.rs create mode 100644 src/ssh/ratatui.rs diff --git a/Cargo.lock b/Cargo.lock index 036370d1..bf2db2be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,8 +14,18 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", - "generic-array", + "crypto-common 0.1.7", + "generic-array 0.14.7", +] + +[[package]] +name = "aead" +version = "0.6.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" +dependencies = [ + "crypto-common 0.2.1", + "inout 0.2.2", ] [[package]] @@ -25,21 +35,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] +[[package]] +name = "aes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +dependencies = [ + "cipher 0.5.1", + "cpubits", + "cpufeatures 0.3.0", +] + [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", + "aead 0.5.2", + "aes 0.8.4", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.1", + "subtle", +] + +[[package]] +name = "aes-gcm" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" +dependencies = [ + "aead 0.6.0-rc.10", + "aes 0.9.0", + "cipher 0.5.1", + "ctr 0.10.0", + "ghash 0.6.0", "subtle", ] @@ -123,6 +158,18 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures 0.2.17", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -155,7 +202,16 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", ] [[package]] @@ -177,6 +233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" dependencies = [ "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] @@ -244,6 +301,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.21.7" @@ -256,15 +319,47 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2 0.12.2", + "sha2 0.10.9", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -289,7 +384,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -298,7 +393,34 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +dependencies = [ + "hybrid-array", ] [[package]] @@ -314,6 +436,16 @@ dependencies = [ "piper", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher 0.4.4", +] + [[package]] name = "bounded-integer" version = "0.5.8" @@ -345,6 +477,12 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "byteorder" version = "1.5.0" @@ -374,7 +512,34 @@ checksum = "3b457277798202ccd365b9c112ebee08ddd57f1033916c8b8ea52f222e5b715d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher 0.4.4", +] + +[[package]] +name = "cbc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98db6aeaef0eeef2c1e3ce9a27b739218825dae116076352ac3777076aa22225" +dependencies = [ + "cipher 0.5.1", ] [[package]] @@ -391,7 +556,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn", + "syn 2.0.117", "tempfile", "toml 0.9.12+spec-1.1.0", ] @@ -427,7 +592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -448,9 +613,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", + "aead 0.5.2", "chacha20 0.9.1", - "cipher", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -472,8 +637,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.1", ] @@ -483,11 +650,22 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", - "inout", + "crypto-common 0.1.7", + "inout 0.1.4", "zeroize", ] +[[package]] +name = "cipher" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.1", + "inout 0.2.2", +] + [[package]] name = "clap" version = "4.6.1" @@ -520,7 +698,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -538,6 +716,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + [[package]] name = "colorchoice" version = "1.0.5" @@ -554,6 +738,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -609,6 +807,27 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -635,6 +854,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -711,27 +936,93 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.1", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-bigint" +version = "0.7.0-rc.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96dacf199529fb801ae62a9aafdc01b189e9504c0d1ee1512a4c16bcd8666a93" +dependencies = [ + "cpubits", + "ctutils", + "getrandom 0.4.2", + "hybrid-array", + "num-traits", + "rand_core 0.10.1", + "serdect", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "getrandom 0.4.2", + "hybrid-array", + "rand_core 0.10.1", +] + +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6081ce8b60c0e533e2bba42771b94eb6149052115f4179744d5779883dc98583" +dependencies = [ + "crypto-bigint", + "libm", + "rand_core 0.10.1", +] + [[package]] name = "crypto_box" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16182b4f39a82ec8a6851155cc4c0cda3065bb1db33651726a29e1951de0f009" dependencies = [ - "aead", + "aead 0.5.2", "crypto_secretbox", "curve25519-dalek 4.1.3", - "salsa20", + "salsa20 0.10.2", "subtle", "zeroize", ] @@ -742,22 +1033,51 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" dependencies = [ - "aead", - "cipher", - "generic-array", + "aead 0.5.2", + "cipher 0.4.4", + "generic-array 0.14.7", "poly1305", - "salsa20", + "salsa20 0.10.2", "subtle", "zeroize", ] +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "ctr" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17469f8eb9bdbfad10f71f4cfddfd38b01143520c0e717d8796ccb4d44d44e42" +dependencies = [ + "cipher 0.5.1", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", + "subtle", ] [[package]] @@ -784,6 +1104,7 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", + "digest 0.11.3", "fiat-crypto 0.3.0", "rustc_version", "subtle", @@ -798,7 +1119,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -821,7 +1142,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", ] [[package]] @@ -832,7 +1153,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -849,6 +1170,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + [[package]] name = "defmt" version = "0.3.100" @@ -878,7 +1205,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -887,7 +1214,35 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror", + "thiserror 2.0.18", +] + +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "const-oid 0.10.2", + "pem-rfc7468 1.0.0", + "zeroize", ] [[package]] @@ -900,17 +1255,52 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.1", + "ctutils", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -919,7 +1309,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -944,7 +1334,16 @@ checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", ] [[package]] @@ -966,11 +1365,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] -name = "either" +name = "ecdsa" +version = "0.17.0-rc.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91bbdd377139884fafcad8dc43a760a3e1e681aa26db910257fa6535b70e1829" +dependencies = [ + "der", + "digest 0.11.3", + "elliptic-curve", + "rfc6979", + "signature", + "spki", + "zeroize", +] + +[[package]] +name = "ed25519" +version = "3.0.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053618a4c3d3bc24f188aa660ae75a46eeab74ef07fb415c61431e5e7cd4749b" +dependencies = [ + "curve25519-dalek 5.0.0-pre.6", + "ed25519", + "rand_core 0.10.1", + "serde", + "sha2 0.11.0", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.14.0-rc.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common 0.2.1", + "digest 0.11.3", + "hkdf 0.13.0", + "hybrid-array", + "once_cell", + "pem-rfc7468 1.0.0", + "pkcs8", + "rand_core 0.10.1", + "rustcrypto-ff", + "rustcrypto-group", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -980,6 +1443,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -997,7 +1472,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1025,6 +1500,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + [[package]] name = "event-listener" version = "5.4.1" @@ -1052,6 +1536,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set 0.5.3", + "regex", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -1073,12 +1567,35 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.9" @@ -1200,7 +1717,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1258,6 +1775,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649" +dependencies = [ + "generic-array 0.14.7", + "rustversion", + "typenum", +] + [[package]] name = "gethostname" version = "1.1.0" @@ -1327,7 +1855,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", - "polyval", + "polyval 0.6.2", +] + +[[package]] +name = "ghash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" +dependencies = [ + "polyval 0.7.1", ] [[package]] @@ -1409,6 +1946,17 @@ dependencies = [ "foldhash 0.1.5", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -1455,13 +2003,28 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", ] [[package]] @@ -1470,7 +2033,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", ] [[package]] @@ -1479,8 +2051,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b05da5b9e5d4720bfb691eebb2b9d42da3570745da71eac8a1f5bb7e59aab88" dependencies = [ - "hmac", - "sha1", + "hmac 0.12.1", + "sha1 0.10.6", ] [[package]] @@ -1546,6 +2118,18 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "ctutils", + "subtle", + "typenum", + "zeroize", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1818,13 +2402,86 @@ dependencies = [ "serde_core", ] +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + [[package]] name = "inout" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "generic-array", + "block-padding 0.3.3", + "generic-array 0.14.7", +] + +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "block-padding 0.4.2", + "hybrid-array", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "internal-russh-forked-ssh-key" +version = "0.6.18+upstream-0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f8a978272e3cbdf4768f7363eb1c8e1e6ba63c52a3ed05e29e222da4aec7cb" +dependencies = [ + "argon2", + "bcrypt-pbkdf", + "crypto-bigint", + "ecdsa", + "ed25519-dalek", + "hex", + "hmac 0.13.0", + "p256", + "p384", + "p521", + "rand_core 0.10.1", + "rsa", + "sec1", + "sha1 0.11.0", + "sha2 0.11.0", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + +[[package]] +name = "internal-russh-num-bigint" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.10.1", + "rand_core 0.10.1", ] [[package]] @@ -1878,7 +2535,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror", + "thiserror 2.0.18", "walkdir", "windows-link 0.2.1", ] @@ -1893,7 +2550,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.117", ] [[package]] @@ -1912,7 +2569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1961,7 +2618,7 @@ dependencies = [ "futures", "glob", "kameo", - "thiserror", + "thiserror 2.0.18", "tokio", ] @@ -1974,9 +2631,46 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "kem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" +dependencies = [ + "crypto-common 0.2.1", + "rand_core 0.10.1", +] + +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + [[package]] name = "lazy_static" version = "1.5.0" @@ -2005,6 +2699,21 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "line-clipping" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +dependencies = [ + "bitflags 2.11.1", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2017,6 +2726,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -2045,12 +2760,31 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix 0.29.0", + "winapi", +] + [[package]] name = "managed" version = "0.8.0" @@ -2084,6 +2818,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + [[package]] name = "memoffset" version = "0.9.1" @@ -2132,10 +2872,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] +[[package]] +name = "ml-kem" +version = "0.3.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8198b5db27ac9773534c371751a59dc18aec8b80aa141e69abfdd1dec2e3f78c" +dependencies = [ + "hybrid-array", + "kem", + "module-lattice", + "rand_core 0.10.1", + "sha3", +] + +[[package]] +name = "module-lattice" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", +] + [[package]] name = "netconfig-rs" version = "0.1.6" @@ -2152,9 +2917,9 @@ dependencies = [ "nix 0.30.1", "scopeguard", "system-configuration-sys", - "thiserror", + "thiserror 2.0.18", "widestring", - "windows", + "windows 0.61.3", ] [[package]] @@ -2201,6 +2966,19 @@ dependencies = [ "log", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.30.1" @@ -2242,11 +3020,11 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c6159f60beb3bbbcdc266bc789bfc6c37fdad7d7ca7152d3e049ef5af633f0" dependencies = [ - "aes-gcm", + "aes-gcm 0.10.3", "blake2", "chacha20poly1305", "noise-protocol", - "sha2", + "sha2 0.10.9", "x25519-dalek 2.0.1", "zeroize", ] @@ -2279,6 +3057,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.1" @@ -2293,7 +3081,16 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", ] [[package]] @@ -2305,6 +3102,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -2329,6 +3135,75 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.14.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "018bfbb86e05fd70a83e985921241035ee09fcd369c4a2c3680b389a01d2ad28" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p384" +version = "0.14.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c91df688211f5957dbe2ab599dcbcaade8d6d3cdc15c5b350d350d7d07ce423" +dependencies = [ + "ecdsa", + "elliptic-curve", + "fiat-crypto 0.3.0", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p521" +version = "0.14.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de6cd9451de522549d36cc78a1b45a699a3d55a872e8ea0c8f0318e502d99e2c" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "pageant" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b537f975f6d8dcf48db368d7ec209d583b015713b5df0f5d92d2631e4ff5595" +dependencies = [ + "byteorder", + "bytes", + "delegate", + "futures", + "log", + "rand 0.8.6", + "sha2 0.10.9", + "thiserror 1.0.69", + "tokio", + "windows 0.62.2", + "windows-strings 0.5.1", +] + [[package]] name = "parking" version = "2.2.1" @@ -2358,59 +3233,160 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.3", + "hmac 0.13.0", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2 0.10.9", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] [[package]] -name = "pest" -version = "2.8.6" +name = "phf_codegen" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "memchr", - "ucd-trie", + "phf_generator", + "phf_shared", ] [[package]] -name = "pest_derive" -version = "2.8.6" +name = "phf_generator" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "pest", - "pest_generator", + "phf_shared", + "rand 0.8.6", ] [[package]] -name = "pest_generator" -version = "2.8.6" +name = "phf_macros" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "pest", - "pest_meta", + "phf_generator", + "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] -name = "pest_meta" -version = "2.8.6" +name = "phf_shared" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "pest", - "sha2", + "siphasher", ] [[package]] @@ -2430,7 +3406,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2450,6 +3426,45 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs5" +version = "0.8.0-rc.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a777c6e26664bc9504b3ce3f6133f8f20d9071f130a4f9fcbd3186959d8dd6" +dependencies = [ + "aes 0.9.0", + "aes-gcm 0.11.0-rc.3", + "cbc 0.2.0", + "der", + "pbkdf2 0.13.0", + "rand_core 0.10.1", + "scrypt", + "sha2 0.11.0", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" +dependencies = [ + "der", + "pkcs5", + "rand_core 0.10.1", + "spki", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -2458,7 +3473,7 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] @@ -2470,7 +3485,18 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "polyval" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eede" +dependencies = [ + "cpubits", + "cpufeatures 0.3.0", + "universal-hash 0.6.1", ] [[package]] @@ -2544,7 +3570,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "primefield" +version = "0.14.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93401c13cc7ff24684571cfca9d3cf9ebabfaf3d4b7b9963ade41ec54da196b5" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.1", + "rand_core 0.10.1", + "rustcrypto-ff", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c5c8a39bcd764bfedf456e8d55e115fe86dda3e0f555371849f2a41cbc9706" +dependencies = [ + "elliptic-curve", ] [[package]] @@ -2566,7 +3615,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2584,12 +3633,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.11.1", "num-traits", "rand 0.9.4", - "rand_chacha", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -2617,7 +3666,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2667,7 +3716,7 @@ checksum = "c23399970eea9c31d0ac84cee4a9d8dd05f89b1da2f4dd5bb44b32a3f66db4f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2698,7 +3747,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2711,7 +3760,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2734,7 +3783,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -2756,7 +3805,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -2807,13 +3856,24 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[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", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -2828,6 +3888,16 @@ 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" @@ -2871,6 +3941,91 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.1", + "compact_str", + "hashbrown 0.16.1", + "indoc", + "itertools", + "kasuari", + "lru", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2897,7 +4052,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2973,6 +4128,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfc6979" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5236ce872cac07e0fb3969b0cbf468c7d2f37d432f1b627dcb7b8d34563fb0c3" +dependencies = [ + "hmac 0.13.0", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -2983,7 +4148,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -3001,6 +4166,128 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rsa" +version = "0.10.0-rc.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb9fd8c1edd9e6a2693623baf0fe77ff05ce022a5d7746900ffc38a15c233de" +dependencies = [ + "const-oid 0.10.2", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1", + "pkcs8", + "rand_core 0.10.1", + "sha2 0.11.0", + "signature", + "zeroize", +] + +[[package]] +name = "russh" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e358980fe9b079b99da387117864ee6f0a3fd02f39e5b5fde6af9c2895374" +dependencies = [ + "aead 0.6.0-rc.10", + "aes 0.8.4", + "aes 0.9.0", + "aes-gcm 0.11.0-rc.3", + "aws-lc-rs", + "bitflags 2.11.1", + "block-padding 0.3.3", + "byteorder", + "bytes", + "cbc 0.1.2", + "cbc 0.2.0", + "cipher 0.5.1", + "crypto-bigint", + "ctr 0.10.0", + "ctr 0.9.2", + "curve25519-dalek 5.0.0-pre.6", + "data-encoding", + "delegate", + "der", + "digest 0.10.7", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "enum_dispatch", + "flate2", + "futures", + "generic-array 1.4.1", + "getrandom 0.2.17", + "ghash 0.6.0", + "hex-literal", + "hkdf 0.13.0", + "hmac 0.12.1", + "hmac 0.13.0", + "inout 0.1.4", + "internal-russh-forked-ssh-key", + "internal-russh-num-bigint", + "keccak", + "log", + "md5", + "ml-kem", + "module-lattice", + "num-bigint", + "p256", + "p384", + "p521", + "pageant", + "pbkdf2 0.12.2", + "pbkdf2 0.13.0", + "pkcs5", + "pkcs8", + "polyval 0.7.1", + "rand 0.10.1", + "rand_core 0.10.1", + "russh-cryptovec", + "russh-util", + "salsa20 0.11.0", + "scrypt", + "sec1", + "sha1 0.10.6", + "sha1 0.11.0", + "sha2 0.10.9", + "sha2 0.11.0", + "sha3", + "signature", + "spki", + "ssh-encoding", + "subtle", + "thiserror 2.0.18", + "tokio", + "typenum", + "universal-hash 0.6.1", + "zeroize", +] + +[[package]] +name = "russh-cryptovec" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36140e8a20297bc2e8338807c3d9ca911f7fa49d7539cbcd6d48d3befd70efd8" +dependencies = [ + "log", + "nix 0.31.3", + "ssh-encoding", + "windows-sys 0.61.2", +] + +[[package]] +name = "russh-util" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" +dependencies = [ + "chrono", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "rustc-hash" version = "2.1.2" @@ -3016,6 +4303,27 @@ dependencies = [ "semver", ] +[[package]] +name = "rustcrypto-ff" +version = "0.14.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" +dependencies = [ + "rand_core 0.10.1", + "subtle", +] + +[[package]] +name = "rustcrypto-group" +version = "0.14.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed" +dependencies = [ + "rand_core 0.10.1", + "rustcrypto-ff", + "subtle", +] + [[package]] name = "rustix" version = "1.1.4" @@ -3051,7 +4359,7 @@ dependencies = [ "inventory", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3126,7 +4434,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -3159,7 +4467,17 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "salsa20" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c" +dependencies = [ + "cfg-if", + "cipher 0.5.1", ] [[package]] @@ -3216,6 +4534,32 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87af57419b594aa23fa95f09f0e06d80d84ba01c26148c43844cad6ff4485f0" +dependencies = [ + "cfg-if", + "pbkdf2 0.13.0", + "salsa20 0.11.0", + "sha2 0.11.0", +] + +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der", + "hybrid-array", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "3.7.0" @@ -3272,7 +4616,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3307,7 +4651,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3360,7 +4704,17 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", ] [[package]] @@ -3371,7 +4725,18 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -3382,23 +4747,65 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ - "lazy_static", + "libc", + "signal-hook-registry", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "signal-hook-mio" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] [[package]] name = "signal-hook-registry" @@ -3410,6 +4817,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core 0.10.1", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -3432,6 +4849,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.12" @@ -3478,6 +4901,45 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "aes 0.8.4", + "aes-gcm 0.10.3", + "cbc 0.1.2", + "chacha20 0.9.1", + "cipher 0.4.4", + "ctr 0.9.2", + "poly1305", + "ssh-encoding", + "subtle", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "bytes", + "pem-rfc7468 0.7.0", + "sha2 0.10.9", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3496,6 +4958,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "stun-rs" version = "0.1.11" @@ -3526,6 +5009,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -3554,7 +5048,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3583,13 +5077,18 @@ name = "tailscale" version = "0.3.3" dependencies = [ "axum", + "bytes", + "chrono", "clap", "include_dir", + "itertools", "mime_guess", "rand 0.10.1", + "ratatui", + "russh", "serde", "serde_json", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", "tracing-subscriber", @@ -3631,13 +5130,96 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom 7.1.3", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bitflags 2.11.1", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix 0.29.0", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf", + "sha2 0.10.9", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -3648,7 +5230,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3668,7 +5250,9 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde_core", "time-core", @@ -3742,7 +5326,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3953,7 +5537,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4023,7 +5607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4157,7 +5741,7 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", @@ -4192,7 +5776,7 @@ dependencies = [ "proptest", "rand 0.10.1", "static_assertions", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tracing", @@ -4261,12 +5845,12 @@ dependencies = [ name = "ts_disco_protocol" version = "0.3.3" dependencies = [ - "aead", + "aead 0.5.2", "crypto_box", "num-derive", "num-traits", "rand 0.10.1", - "thiserror", + "thiserror 2.0.18", "ts_keys", "zerocopy", ] @@ -4320,7 +5904,7 @@ dependencies = [ "httparse", "hyper", "hyper-util", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tracing", @@ -4336,7 +5920,7 @@ version = "0.3.3" dependencies = [ "crypto_box", "serde", - "thiserror", + "thiserror 2.0.18", "x25519-dalek 3.0.0-pre.6", "zerocopy", ] @@ -4350,7 +5934,7 @@ dependencies = [ "reqwest", "serde_json", "stun-rs", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", "tracing-test", @@ -4391,7 +5975,7 @@ dependencies = [ "flume", "heapless", "smoltcp", - "thiserror", + "thiserror 2.0.18", "tracing", "tracing-panic", "ts_cli_util", @@ -4499,7 +6083,7 @@ dependencies = [ "ipnet", "kameo", "kameo_actors", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", "ts_bart", @@ -4558,7 +6142,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tracing", @@ -4582,7 +6166,7 @@ dependencies = [ "bytes", "ipnet", "serde", - "thiserror", + "thiserror 2.0.18", "tracing", "ts_hexdump", "ts_packet", @@ -4594,14 +6178,14 @@ dependencies = [ name = "ts_tunnel" version = "0.3.3" dependencies = [ - "aead", + "aead 0.5.2", "base64 0.22.1", "blake2", "bytes", "chacha20poly1305", "clap", "hex", - "hkdf", + "hkdf 0.12.4", "itertools", "rand 0.10.1", "tokio", @@ -4699,6 +6283,29 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -4711,10 +6318,26 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "universal-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" +dependencies = [ + "crypto-common 0.2.1", + "ctutils", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -4746,6 +6369,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "atomic", + "getrandom 0.4.2", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" @@ -4758,6 +6393,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + [[package]] name = "wait-timeout" version = "0.2.1" @@ -4852,7 +6496,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -4937,12 +6581,100 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2 0.10.9", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + [[package]] name = "widestring" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -4952,17 +6684,35 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -4974,6 +6724,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -5008,7 +6767,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -5019,7 +6789,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5030,7 +6800,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5055,6 +6825,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.6.1" @@ -5180,6 +6960,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5334,7 +7123,7 @@ dependencies = [ "heck", "indexmap 2.14.0", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5350,7 +7139,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5440,7 +7229,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -5461,7 +7250,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5481,7 +7270,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -5502,7 +7291,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5535,7 +7324,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 323243d2..e45842fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -205,7 +205,11 @@ ts_netstack_smoltcp = { workspace = true, features = ["tokio"] } ts_keys.workspace = true axum = { version = "0.8", optional = true } +bytes = { workspace = true, optional = true } rand.workspace = true +ratatui = { version = "0.30", optional = true } +# exclude rsa to avoid https://rustsec.org/advisories/RUSTSEC-2023-0071 +russh = { version = "0.60", optional = true, default-features = false, features = ["flate2", "aws-lc-rs"] } serde.workspace = true serde_json.workspace = true thiserror.workspace = true @@ -215,8 +219,10 @@ url.workspace = true [dev-dependencies] # Dependencies for examples -clap = { workspace = true, features = ["derive", "env"] } +clap = { workspace = true, features = ["derive", "env", "string"] } +chrono.workspace = true include_dir = "0.7" +itertools.workspace = true mime_guess = "2.0" tokio = { workspace = true, features = ["full"] } tracing = { workspace = true, features = ["release_max_level_info"] } @@ -233,6 +239,7 @@ workspace = true # Enable the `axum` module, which enables you to run an `axum` HTTP server on top of a tailscale TCP # listener. axum = ["dep:axum"] +ssh = ["dep:russh", "dep:bytes", "dep:ratatui"] [[example]] name = "axum" @@ -243,3 +250,7 @@ name = "peer_ping" [[example]] name = "tcp_echo" + +[[example]] +name = "ssh_peer_lookup" +required-features = ["ssh"] diff --git a/examples/ssh_peer_lookup/README.md b/examples/ssh_peer_lookup/README.md new file mode 100644 index 00000000..2d2425c3 --- /dev/null +++ b/examples/ssh_peer_lookup/README.md @@ -0,0 +1,22 @@ +# example: ssh peer lookup + +Run an SSH server hosting a TUI that lets connecting clients look up info about peers in the +tailnet. + +Please be aware there's currently no auth checking implemented beyond what's globally provided by +tailscale-rs and network packet filters. The ssh policy file block is not consulted. + +The server key is randomized on each start, so you will likely want to connect using +`-o StrictHostKeyChecking=no`. + +## Example usage + +```shell +$ cargo run --example ssh_peer_lookup --features ssh -- -k $MY_AUTH_KEY -c $MY_CONFIG_FILE +... +INFO tailscale::ssh: ssh server listening listen_addr=$TAILNET_IP:1234 +... + +# in another terminal: +$ ssh $TAILNET_IP -p 1234 -o StrictHostKeyChecking=no +``` diff --git a/examples/ssh_peer_lookup/main.rs b/examples/ssh_peer_lookup/main.rs new file mode 100644 index 00000000..8ebb66e6 --- /dev/null +++ b/examples/ssh_peer_lookup/main.rs @@ -0,0 +1,234 @@ +//! Run an SSH server hosting a custom TUI console that lets clients look up info about +//! peers in the tailnet. + +use std::{collections::VecDeque, net::IpAddr, path::PathBuf, sync::Arc}; + +use chrono::Datelike; +use clap::Parser; +use itertools::Itertools; +use ratatui::{ + Frame, + layout::{Constraint, Layout}, + macros::span, + prelude::{Line, Span}, + style::{Style, Stylize}, + text::{Text, ToSpan}, + widgets::{Block, List, ListItem, Paragraph}, +}; +use russh::keys::Algorithm; +use tailscale::ssh; +use tracing_subscriber::filter::LevelFilter; +use ts_control::Node; + +/// Run an SSH server running a custom console over the tailnet supporting peer ip lookups. +/// +/// This does _no_ authentication -- anyone on the tailnet permitted to talk to the relevant +/// port can connect. +#[derive(clap::Parser)] +#[command(version, about)] +struct Args { + /// Path to a key file to use. Will be created if it doesn't exist. + #[arg(short = 'c', long, default_value = "tsrs_keys.json")] + key_file: PathBuf, + + /// The auth key to connect with. + /// + /// Can be omitted if the key file is already authenticated. + #[arg(short = 'k', long)] + auth_key: Option, + + /// Port to listen on (on tailnet IPv4). + #[clap(short, long, default_value_t = 1234)] + listen_port: u16, +} + +#[derive(Default)] +struct PeerLookupTui { + input_state: String, + messages: VecDeque<(String, Option)>, +} + +impl ssh::RatatuiApp for PeerLookupTui { + async fn input(&mut self, data: &[u8], env: impl ssh::RatatuiEnv) { + let new_data = String::from_utf8_lossy(data); + + // NOTE(npry): this is essentially a manual terminal event parser. Ideally we'd hook this up + // to one of the terminal crates' existing parsers, but none of them expose it. `crossterm` + // (which we're using as our backend) has all the machinery to do it, but it's not exposed + // as part of their API; instead, it's hardcoded to a system-specific implementation. + // + // Issue tracking this (https://github.com/crossterm-rs/crossterm/issues/694) has been open + // since 2022. + for c in new_data.chars() { + match c { + // ^C, ^D + '\u{3}' | '\u{4}' => { + tracing::debug!("got ^C or ^D, closing terminal"); + env.close().await; + return; + } + + // BKSP + '\u{8}' | '\u{7f}' => { + if let Some((idx, _)) = self.input_state.char_indices().next_back() { + self.input_state.truncate(idx); + } + } + + // ESC + '\u{1b}' => { + // punt, not implementing a full control sequence parser here + } + + '\r' | '\n' => { + let line = core::mem::take(&mut self.input_state); + if line.is_empty() { + continue; + } + + tracing::trace!(query = line); + + let peer = env.tailscale().peer_by_name(&line).await.ok().flatten(); + + self.messages.truncate(31); + self.messages.push_front((line, peer)); + } + c if !c.is_control() => { + self.input_state.push(c); + } + _ignore => {} + } + } + } + + fn draw(&mut self, frame: &mut Frame) { + let layout = Layout::vertical([Constraint::Length(3), Constraint::Min(1)]); + + let [input_area, msg_area] = frame.area().layout(&layout); + + let input = Paragraph::new(Line::from_iter([ + Span::raw(&self.input_state), + '█'.slow_blink(), + ])) + .style(Style::default()) + .block(Block::bordered().title("peer query")); + + frame.render_widget(input, input_area); + + #[allow(unstable_name_collisions)] + let messages = self + .messages + .iter() + .map(|(query, node)| ListItem::new(render_node(query, node.as_ref()))) + .intersperse(ListItem::new("")) + .collect::>(); + + let messages = List::new(messages).block(Block::bordered().title("results")); + + frame.render_widget(messages, msg_area); + } +} + +fn render_node<'a>(query: &'a str, node: Option<&'a Node>) -> Text<'a> { + let Some(node) = node else { + return Text::from_iter([Line::from_iter([ + span!(Style::new().red().bold(); "{query}"), + span!(": no match"), + ])]); + }; + + let mut text = Text::from_iter([ + Line::from_iter([ + span!(Style::new().green().bold(); "{} ", node.fqdn(false)), + span!("({})", node.stable_id.0), + ":".into(), + ]), + Line::from_iter([ + "ipv4: ".into(), + node.tailnet_address.ipv4.to_span().light_cyan(), + ]), + Line::from_iter([ + "ipv6: ".into(), + node.tailnet_address.ipv6.to_span().light_cyan(), + ]), + Line::from_iter([ + "node key: ".into(), + node.node_key.to_span().yellow(), + " (expires ".into(), + if let Some(nk) = &node.node_key_expiry { + span!("{}/{}/{}", nk.year(), nk.month(), nk.day()) + } else { + "never".red() + }, + ")".into(), + ]), + ]); + + if let Some(disco_key) = &node.disco_key { + text.push_line(Line::from_iter([ + "disco key: ".into(), + disco_key.to_span().yellow(), + ])); + } + + if let Some(derp_region) = &node.derp_region { + text.push_line(Line::from_iter([ + "derp region: ".into(), + derp_region.to_span().light_cyan(), + ])); + } + + if !node.tags.is_empty() { + let mut line = Line::raw("tags: "); + + #[allow(unstable_name_collisions)] + line.extend( + node.tags + .iter() + .map(Span::raw) + .map(|span| span.light_blue()) + .intersperse(Span::raw(", ")), + ); + + text.push_line(line); + } + + text +} + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .init(); + + let args = Args::parse(); + + let dev = tailscale::Device::new( + &tailscale::Config::default_with_key_file(&args.key_file).await?, + args.auth_key, + ) + .await?; + + let ipv4: IpAddr = dev.ipv4_addr().await?.into(); + let dev = Arc::new(dev); + + dev.serve_ssh_tui::( + russh::server::Config { + keys: vec![ + russh::keys::PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519).unwrap(), + ], + methods: russh::MethodSet::from(&[russh::MethodKind::None][..]), + nodelay: true, + ..Default::default() + }, + (ipv4, args.listen_port).into(), + ) + .await?; + + Ok(()) +} diff --git a/flake.nix b/flake.nix index 61fe91dd..0bbce771 100644 --- a/flake.nix +++ b/flake.nix @@ -244,8 +244,25 @@ fmt = pkgs.craneLibNightlyFmt.cargoFmt common; # Consults rustsec advisory db for reported vulnerabilities in dependencies + # + # We run this in addition to `cargo deny` because the deny check can't download the advisory + # db inside Nix -- for whatever reason, `cargo audit` has that hooked up in crane, while + # `deny` doesn't. Notably, the behavior of `cargo audit` appears to deviate from that of + # `deny`, so we ignore some advisories here which are known to be fine when checked via + # `deny`. audit = pkgs.craneLib.cargoAudit (common // { advisory-db = inputs.rust-advisory-db; + + cargoAuditExtraArgs = let + ignored = [ + # default ignore in crane + "yanked" + + # old version of `rsa` used through russh, feature is turned off (deny doesn't mind) + "RUSTSEC-2023-0071" + ]; + + in "--ignore " + (builtins.concatStringsSep " --ignore " ignored); }); # This does the same as `cargo test`, it's just a pretty harness diff --git a/src/lib.rs b/src/lib.rs index ccba5d9a..f2240378 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,6 +142,8 @@ use ts_netstack_smoltcp::{CreateSocket, netcore::Channel}; pub mod axum; pub mod config; mod error; +#[cfg(feature = "ssh")] +pub mod ssh; /// How a program connects to a tailnet and communicates with peers. /// diff --git a/src/ssh/channel_server.rs b/src/ssh/channel_server.rs new file mode 100644 index 00000000..7230eef7 --- /dev/null +++ b/src/ssh/channel_server.rs @@ -0,0 +1,291 @@ +use std::{collections::HashMap, marker::PhantomData, net::SocketAddr, sync::Arc}; + +use russh::{ + Channel, ChannelId, Pty, Sig, + server::{Auth, Handle, Msg, Session}, +}; +use tokio::{ + sync::{mpsc, mpsc::UnboundedSender}, + task::JoinSet, +}; + +use crate::{Device, ssh::TailnetServer}; + +type Request = (ChannelId, ChannelEvent); + +/// Handler for a channel session. +pub trait ChannelHandler: Sized { + /// Error this handler produces. + type Error: Into + std::error::Error; + + /// Construct a new per-channel handler. + fn new( + handle: tokio::runtime::Handle, + channel_id: ChannelId, + session: Handle, + dev: Arc, + ) -> Result; + + /// Handle an event from the channel. + fn handle_event( + &mut self, + event: &ChannelEvent, + ) -> impl Future> + Send; +} + +/// Implementation of [`russh::server::Handler`] which provides per-channel session +/// handlers using a parametric [`ChannelHandler`]. +/// +/// Primary motivation is to support custom console or TUI sessions over tailnet SSH +/// connections. +/// +/// # Authentication and authorization +/// +/// **WARNING**: [`ChannelServer`] currently does _no_ authentication or authorization checks +/// internally. It is still protected by the policy file's network-layer rules, but it does +/// not currently support the `ssh` policy block or application capabilities. +pub struct ChannelServer { + channel_state: HashMap, + remote: SocketAddr, + dev: Arc, + _handler: PhantomSend, +} + +struct PhantomSend(PhantomData); + +// SAFETY: H is a phantom type, it's never actually sent +unsafe impl Send for PhantomSend {} + +#[derive(thiserror::Error, Debug, Copy, Clone, PartialEq, Eq)] +#[error("no such channel")] +struct NoChannel; + +/// State of a channel in [`ChannelServer`]. +struct ChannelState { + channel: ChannelId, + tx: UnboundedSender, + _joinset: JoinSet<()>, +} + +impl ChannelState { + fn send(&self, event: ChannelEvent) { + if self.tx.send((self.channel, event)).is_err() { + tracing::error!(channel = %self.channel, "failed to send event"); + } + } +} + +impl ChannelServer { + fn get_channel( + &mut self, + id: ChannelId, + ) -> Result<&mut ChannelState, Box> { + self.channel_state.get_mut(&id).ok_or(Box::new(NoChannel)) + } +} + +impl TailnetServer for ChannelServer { + fn new_client(dev: Arc, addr: SocketAddr) -> Self { + Self { + channel_state: Default::default(), + dev, + remote: addr, + _handler: PhantomSend(PhantomData), + } + } +} + +/// An event that may be generated by a channel connected to a [`ChannelServer`]. +#[derive(Debug, Clone)] +pub enum ChannelEvent { + /// Data was received over the channel. + Data(Vec), + /// A resize event occurred. + Resize { + /// The new width of the tty. + width: u16, + /// The new height of the tty. + height: u16, + }, + /// A signal was sent over the channel. + Signal(Sig), + /// The channel was closed. + Close, + /// The channel received EOF. + Eof, +} + +impl russh::server::Handler for ChannelServer +where + H: ChannelHandler + Send, + H::Error: Send, +{ + type Error = Box; + + #[tracing::instrument(skip_all, fields(user = %_user, remote = ?self.remote))] + async fn auth_none(&mut self, _user: &str) -> Result { + let Some(peer) = self.dev.peer_by_tailnet_ip(self.remote.ip()).await? else { + tracing::error!("remote ip does not match a known peer"); + return Ok(Auth::reject()); + }; + + tracing::debug!(?peer, "accept ssh connection"); + + Ok(Auth::Accept) + } + + async fn channel_open_session( + &mut self, + channel: Channel, + session: &mut Session, + ) -> Result { + tracing::debug!(channel = ?channel.id(), "new session"); + + let (tx, mut rx) = mpsc::unbounded_channel::(); + let mut joinset = JoinSet::new(); + + let (channel_id, session_handle) = (channel.id(), session.handle()); + let dev = self.dev.clone(); + + joinset.spawn(async move { + let rt = tokio::runtime::Handle::current(); + + let mut handler = match H::new(rt, channel_id, session_handle.clone(), dev) { + Ok(handler) => handler, + Err(e) => { + let e = e.into(); + tracing::error!(error = %e, %channel_id, "spawning channel handler"); + + if session_handle.close(channel_id).await.is_err() { + tracing::error!("failed closing channel after handler init error"); + }; + + return; + } + }; + + while let Some((_channel, evt)) = rx.recv().await { + let result = handler.handle_event(&evt).await; + + if let Err(e) = result { + let e = e.into(); + tracing::error!(error = %e, %channel_id, ?evt, "handling event"); + + if session_handle.close(channel_id).await.is_err() { + tracing::error!("failed closing channel after event handler error"); + }; + + break; + } + } + + tracing::debug!(?channel_id, "closed"); + }); + + self.channel_state.insert( + channel.id(), + ChannelState { + channel: channel.id(), + tx, + _joinset: joinset, + }, + ); + + session.channel_success(channel.id())?; + + Ok(true) + } + + async fn channel_close( + &mut self, + channel: ChannelId, + session: &mut Session, + ) -> Result<(), Self::Error> { + tracing::trace!(?channel, "session closed"); + + self.get_channel(channel)?.send(ChannelEvent::Close); + self.channel_state.remove(&channel); + + session.channel_success(channel)?; + + Ok(()) + } + + async fn signal( + &mut self, + channel: ChannelId, + signal: Sig, + session: &mut Session, + ) -> Result<(), Self::Error> { + self.get_channel(channel)? + .send(ChannelEvent::Signal(signal)); + session.channel_success(channel)?; + + Ok(()) + } + + async fn data( + &mut self, + channel: ChannelId, + data: &[u8], + session: &mut Session, + ) -> Result<(), Self::Error> { + self.get_channel(channel)? + .send(ChannelEvent::Data(data.into())); + + session.channel_success(channel)?; + + Ok(()) + } + + async fn channel_eof( + &mut self, + channel: ChannelId, + session: &mut Session, + ) -> Result<(), Self::Error> { + self.get_channel(channel)?.send(ChannelEvent::Eof); + session.channel_success(channel)?; + + Ok(()) + } + + async fn window_change_request( + &mut self, + channel: ChannelId, + col_width: u32, + row_height: u32, + _: u32, + _: u32, + session: &mut Session, + ) -> Result<(), Self::Error> { + self.get_channel(channel)?.send(ChannelEvent::Resize { + width: col_width as _, + height: row_height as _, + }); + + session.channel_success(channel)?; + + Ok(()) + } + + async fn pty_request( + &mut self, + channel: ChannelId, + _: &str, + col_width: u32, + row_height: u32, + _: u32, + _: u32, + _: &[(Pty, u32)], + session: &mut Session, + ) -> Result<(), Self::Error> { + self.get_channel(channel)?.send(ChannelEvent::Resize { + width: col_width as _, + height: row_height as _, + }); + + session.channel_success(channel)?; + + Ok(()) + } +} diff --git a/src/ssh/channel_write.rs b/src/ssh/channel_write.rs new file mode 100644 index 00000000..0b065276 --- /dev/null +++ b/src/ssh/channel_write.rs @@ -0,0 +1,46 @@ +use std::io::{ErrorKind, Write}; + +use bytes::BytesMut; + +/// Wrapper that implements [`Write`] for a [`russh`] channel. +/// +/// Needed to support a `crossterm` terminal driver, which isn't async-aware. +/// +/// This is basically `tokio_util::SyncIoBridge`, except that [`russh::server::Handle`] +/// doesn't natively support [`tokio::io::AsyncWrite`], so it's all just done here. +pub struct ChannelWrite { + rt: tokio::runtime::Handle, + buf: BytesMut, + channel_id: russh::ChannelId, + handle: russh::server::Handle, +} + +impl ChannelWrite { + pub fn new( + runtime: tokio::runtime::Handle, + handle: russh::server::Handle, + channel_id: russh::ChannelId, + ) -> Self { + Self { + rt: runtime, + buf: BytesMut::new(), + handle, + channel_id, + } + } +} + +impl Write for ChannelWrite { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + tokio::task::block_in_place(|| { + self.rt + .block_on(self.handle.data(self.channel_id, self.buf.split())) + }) + .map_err(|_| ErrorKind::BrokenPipe.into()) + } +} diff --git a/src/ssh/mod.rs b/src/ssh/mod.rs new file mode 100644 index 00000000..13639ca4 --- /dev/null +++ b/src/ssh/mod.rs @@ -0,0 +1,112 @@ +//! Support for tailnet-native, in-process SSH servers. +//! +//! # Overview +//! +//! This module (`tailscale::ssh`) holds helpers for running SSH servers on the tailnet +//! using [`russh`]. They delegate their functionality to the [`Handler`] trait, which is +//! `russh`'s notion of a _connection_ handler, i.e. a single incoming TCP connection gets +//! a single instance of [`Handler`]. +//! +//! ## Channels +//! +//! SSH has a nested notion of channels, which are multiplexed over a single connection. +//! The terminal session you open over a normal machine-to-machine ssh connection runs in a +//! channel, and in principle, you can have multiple channels open on the same connection. +//! +//! The `channel_server` module provides a [`ChannelServer`] type that separates out the +//! per-channel handler logic from `russh`'s monolithic [`Handler`]. Channel handler logic +//! is supported here by [`ChannelHandler`], which is passed into [`ChannelServer`] and +//! processes a [`ChannelEvent`] stream for each channel that's opened. +//! +//! ## Terminal applications +//! +//! Support for building per-channel terminal application is provided by [`RatatuiTerm`], +//! which implements [`ChannelHandler`] to drive a +//! [`ratatui::Terminal`][::ratatui::Terminal]. The user provides an implementation of +//! [`RatatuiApp`] that consumes input data and supports draws to the screen, and the +//! [`RatatuiTerm`] drives it automatically. + +pub extern crate russh; + +use std::{fmt::Debug, net::SocketAddr, sync::Arc}; + +use russh::server::Handler; + +mod channel_server; +mod channel_write; +mod ratatui; + +pub use channel_server::{ChannelEvent, ChannelHandler, ChannelServer}; +pub use ratatui::{RatatuiApp, RatatuiEnv, RatatuiTerm}; + +/// Trait to construct a new [`Handler`] from a Tailscale [`Device`][crate::Device] and +/// the address of a connecting client. +/// +/// Rephrasing of [`russh::server::Server`] that includes the Tailscale device as an +/// argument and skips the support for off-tailnet IP and Unix sockets. +pub trait TailnetServer { + /// Construct a new handler. + fn new_client(dev: Arc, addr: SocketAddr) -> Self; +} + +impl crate::Device { + /// Serve an ssh service on the given TCP address. + /// + /// This is a minimal helper that just wires up the relevant pieces. All the + /// authentication and actual SSH server logic must be implemented by the caller in + /// the `TailnetServer` (`H`) and configured by `config`. + pub async fn serve_ssh( + self: Arc, + config: russh::server::Config, + listen_addr: SocketAddr, + ) -> Result<(), crate::Error> + where + H: TailnetServer + Handler + Send + 'static, + H::Error: Debug, + { + let config = Arc::new(config); + let listener = self.tcp_listen(listen_addr).await?; + + tracing::info!(%listen_addr, "ssh server listening"); + + loop { + let conn = listener.accept().await?; + + let handler = H::new_client(self.clone(), conn.remote_addr()); + let config = config.clone(); + + tokio::task::spawn(async move { + let sess = match russh::server::run_stream(config, conn, handler).await { + Ok(sess) => sess, + Err(e) => { + tracing::error!(error = ?e, "establishing session"); + return; + } + }; + + match sess.await { + Ok(()) => {} + Err(e) => { + tracing::error!(error = ?e, "running ssh session"); + } + } + }); + } + } + + /// Serve an SSH TUI service on the given TCP address. + /// + /// Wrapper around [`serve_ssh`][crate::Device::serve_ssh] to specifically use + /// [`ChannelServer`] around a [`RatatuiTerm`] using `App`. + pub async fn serve_ssh_tui( + self: Arc, + config: russh::server::Config, + listen_addr: SocketAddr, + ) -> Result<(), crate::Error> + where + App: RatatuiApp + Default + Send + 'static, + { + self.serve_ssh::>>(config, listen_addr) + .await + } +} diff --git a/src/ssh/ratatui.rs b/src/ssh/ratatui.rs new file mode 100644 index 00000000..6559f191 --- /dev/null +++ b/src/ssh/ratatui.rs @@ -0,0 +1,160 @@ +use std::sync::Arc; + +use ratatui::{Terminal, TerminalOptions, Viewport, backend::CrosstermBackend, layout::Rect}; +use russh::{ChannelId, Sig, server::Handle}; + +use crate::{ + Device, + ssh::{ChannelEvent, ChannelHandler, channel_write::ChannelWrite}, +}; + +type Backend = CrosstermBackend; + +/// Terminal environment for [`RatatuiApp`]. +pub trait RatatuiEnv { + /// Request that the terminal close. + fn close(&self) -> impl Future + Send; + + /// Get a reference to the Tailscale [`Device`] this is running in. + fn tailscale(&self) -> &Device; +} + +/// A [`ratatui`] application designed to be driven by a +/// [`ChannelServer`][crate::ssh::ChannelServer]. +pub trait RatatuiApp { + /// Process new input from the channel. + fn input( + &mut self, + data: &[u8], + env: impl RatatuiEnv + Send, + ) -> impl Future + Send; + + /// Render the app to the [`ratatui::Frame`]. + fn draw(&mut self, frame: &mut ratatui::Frame); +} + +/// A [`ChannelHandler`] that runs a [`RatatuiApp`]. +pub struct RatatuiTerm { + channel_id: ChannelId, + session: Handle, + term: Terminal, + dev: Arc, + io: Io, +} + +struct Env<'a> { + channel_id: ChannelId, + session: &'a Handle, + dev: &'a Device, +} + +impl RatatuiEnv for Env<'_> { + async fn close(&self) { + if self.session.close(self.channel_id).await.is_err() { + tracing::error!("channel closed while closing ratatui app"); + } + } + + fn tailscale(&self) -> &Device { + self.dev + } +} + +impl RatatuiTerm +where + Io: RatatuiApp, +{ + fn refresh(&mut self) -> std::io::Result<()> { + self.term.clear()?; + self.draw()?; + + Ok(()) + } + + fn draw(&mut self) -> std::io::Result<()> { + self.term.draw(|frame| self.io.draw(frame))?; + + Ok(()) + } +} + +impl ChannelHandler for RatatuiTerm +where + Io: RatatuiApp + Default + Send, +{ + type Error = std::io::Error; + + fn new( + rt: tokio::runtime::Handle, + channel_id: ChannelId, + session: Handle, + dev: Arc, + ) -> Result { + let mut term = Self { + term: make_term(rt, session.clone(), channel_id)?, + dev, + channel_id, + session, + io: Default::default(), + }; + term.refresh()?; + + Ok(term) + } + + async fn handle_event(&mut self, event: &ChannelEvent) -> Result<(), Self::Error> { + match event { + ChannelEvent::Data(d) => { + self.io + .input( + d, + Env { + dev: &self.dev, + channel_id: self.channel_id, + session: &self.session, + }, + ) + .await; + + self.draw()?; + } + ChannelEvent::Resize { width, height } => { + self.term.resize(Rect::new(0, 0, *width, *height))?; + self.draw()?; + } + ChannelEvent::Eof + | ChannelEvent::Signal(Sig::ABRT | Sig::QUIT | Sig::TERM | Sig::KILL | Sig::INT) => { + tracing::debug!(?event, channel_id = %self.channel_id, "close channel"); + + if self.session.close(self.channel_id).await.is_err() { + tracing::error!("session already shut down"); + + return Err(std::io::ErrorKind::BrokenPipe.into()); + } + } + ChannelEvent::Signal(sig) => { + tracing::debug!(?sig, "unhandled signal"); + } + ChannelEvent::Close => { + self.term.clear()?; + } + } + + Ok(()) + } +} + +fn make_term( + rt: tokio::runtime::Handle, + session_handle: Handle, + channel_id: ChannelId, +) -> Result, ::Error> { + let terminal_handle = ChannelWrite::new(rt, session_handle, channel_id); + let backend = CrosstermBackend::new(terminal_handle); + + let options = TerminalOptions { + viewport: Viewport::Fixed(Rect::default()), + }; + + Terminal::with_options(backend, options) +}