diff --git a/.gitignore b/.gitignore index 2416999..d020f26 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,8 @@ target/ # Added by cargo /target -.DS_Store \ No newline at end of file +.DS_Store + + +/tests/relay_urls/certs +/tests/relay_urls/rcgen diff --git a/Cargo.lock b/Cargo.lock index 7973486..16a2c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,19 @@ dependencies = [ "inout", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -117,6 +130,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "arrayref" version = "0.3.9" @@ -129,6 +148,45 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "async-compat" version = "0.2.4" @@ -196,6 +254,92 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +dependencies = [ + "axum-core", + "axum-macros", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "axum-server" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http 1.3.1", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "backon" version = "1.5.1" @@ -240,6 +384,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-url" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2b6c78c06f7288d5e3c3d683bde35a79531127c83b087e5d0d77c974b4b28" +dependencies = [ + "base64", +] + [[package]] name = "base64ct" version = "1.8.0" @@ -374,9 +527,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" dependencies = [ "clap_builder", "clap_derive", @@ -384,9 +537,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" dependencies = [ "anstream", "anstyle", @@ -396,9 +549,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -511,6 +664,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "critical-section" version = "1.2.0" @@ -638,6 +806,20 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -665,6 +847,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.4.0" @@ -743,6 +939,27 @@ dependencies = [ "crypto-common 0.2.0-rc.4", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -868,6 +1085,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + [[package]] name = "errno" version = "0.3.13" @@ -896,12 +1122,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -917,6 +1160,26 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror 1.0.69", +] + +[[package]] +name = "fs-err" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad492b2cf1d89d568a43508ab24f98501fe03f2f31c01e1d0fe7366a71745d2" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "futures" version = "0.3.31" @@ -1014,6 +1277,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -1101,6 +1370,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "governor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "getrandom 0.3.3", + "hashbrown 0.15.4", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.9.2", + "smallvec", + "spinning_top", + "web-time", +] + [[package]] name = "h2" version = "0.4.11" @@ -1129,11 +1421,22 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] [[package]] name = "hashbrown" @@ -1143,7 +1446,7 @@ checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1188,6 +1491,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustls", + "serde", "thiserror 2.0.12", "tinyvec", "tokio", @@ -1212,6 +1516,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "rustls", + "serde", "smallvec", "thiserror 2.0.12", "tokio", @@ -1219,6 +1524,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "hickory-server" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53e5fe811b941c74ee46b8818228bfd2bc2688ba276a0eaeb0f2c95ea3b2585" +dependencies = [ + "async-trait", + "bytes", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-util", + "h2", + "hickory-proto", + "hickory-resolver", + "http 1.3.1", + "ipnet", + "prefix-trie", + "rustls", + "serde", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", +] + [[package]] name = "home" version = "0.5.11" @@ -1297,6 +1630,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hybrid-array" version = "0.4.5" @@ -1569,6 +1918,9 @@ name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +dependencies = [ + "serde", +] [[package]] name = "iri-string" @@ -1657,18 +2009,71 @@ dependencies = [ "zeroize_derive", ] +[[package]] +name = "iroh-dns-server" +version = "0.94.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407bc80c0f76857e0cd2445ca35787fd0819a639ef5f3ed3b1dcffe5fc7ea92" +dependencies = [ + "async-trait", + "axum", + "axum-server", + "base64-url", + "bytes", + "clap", + "derive_more 2.0.1", + "dirs-next", + "governor", + "hickory-server", + "http 1.3.1", + "humantime", + "humantime-serde", + "iroh-metrics", + "lru 0.16.2", + "n0-future", + "n0-snafu", + "pkarr", + "rcgen", + "redb", + "regex", + "rustls", + "rustls-pemfile", + "serde", + "snafu", + "struct_iterable", + "strum", + "tokio", + "tokio-rustls", + "tokio-rustls-acme", + "tokio-stream", + "tokio-util", + "toml", + "tower-http", + "tower_governor", + "tracing", + "tracing-subscriber", + "ttl_cache", + "url", + "z32", +] + [[package]] name = "iroh-metrics" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "090161e84532a0cb78ab13e70abb882b769ec67cf5a2d2dcea39bd002e1f7172" dependencies = [ + "http-body-util", + "hyper", + "hyper-util", "iroh-metrics-derive", "itoa", "postcard", + "reqwest", "ryu", "serde", "snafu", + "tokio", "tracing", ] @@ -1717,6 +2122,7 @@ dependencies = [ "rustc-hash", "rustls", "rustls-pki-types", + "rustls-platform-verifier", "slab", "thiserror 2.0.12", "tinyvec", @@ -1744,9 +2150,12 @@ version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "360e201ab1803201de9a125dd838f7a4d13e6ba3a79aeb46c7fbf023266c062e" dependencies = [ + "ahash", "blake3", "bytes", "cfg_aliases", + "clap", + "dashmap", "data-encoding", "derive_more 2.0.1", "getrandom 0.3.3", @@ -1768,19 +2177,29 @@ dependencies = [ "pkarr", "postcard", "rand 0.9.2", + "rcgen", + "reloadable-state", "reqwest", "rustls", + "rustls-cert-file-reader", + "rustls-cert-reloadable-resolver", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_bytes", "sha1", + "simdutf8", "snafu", "strum", + "time", "tokio", "tokio-rustls", + "tokio-rustls-acme", "tokio-util", "tokio-websockets", + "toml", "tracing", + "tracing-subscriber", "url", "webpki-roots", "ws_stream_wasm", @@ -1796,6 +2215,8 @@ dependencies = [ "ed25519-dalek 2.2.0", "homedir", "iroh", + "iroh-dns-server", + "iroh-relay", "rand 0.9.2", "runas", "self-runas", @@ -1807,7 +2228,7 @@ dependencies = [ "tracing-subscriber", "whoami", "windows-service", - "windows-sys 0.61.1", + "windows-sys 0.61.2", "z32", ] @@ -1878,6 +2299,12 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1952,6 +2379,28 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mainline" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be6c12ff79bfbf65bcbec84882a4bf700177df6d83a7b866c6a01cda7db4777" +dependencies = [ + "crc", + "document-features", + "dyn-clone", + "ed25519-dalek 3.0.0-pre.1", + "flume", + "futures-lite", + "getrandom 0.3.3", + "lru 0.16.2", + "serde", + "serde_bencode", + "serde_bytes", + "sha1_smol", + "thiserror 2.0.12", + "tracing", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1961,12 +2410,30 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2175,6 +2642,28 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "ntimestamp" version = "1.0.0" @@ -2199,12 +2688,31 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2245,6 +2753,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2302,6 +2819,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "1.0.0-rc.3" @@ -2377,6 +2904,7 @@ dependencies = [ "getrandom 0.3.3", "log", "lru 0.13.0", + "mainline", "ntimestamp", "reqwest", "self_cell", @@ -2506,6 +3034,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cf4c7c25f1dd66c76b451e9041a8cfce26e4ca754934fa7aed8d5a59a01d20" +dependencies = [ + "ipnet", + "num-traits", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -2524,6 +3062,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quinn" version = "0.11.8" @@ -2653,6 +3206,38 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rcgen" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fae430c6b28f1ad601274e78b7dffa0546de0b73b4cd32f46723c0c2a16f7a5" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + +[[package]] +name = "redb" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eca1e9d98d5a7e9002d0013e18d5a9b000aee942eb134883a82f06ebffb6c01" +dependencies = [ + "libc", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -2662,11 +3247,34 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -2679,6 +3287,23 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reloadable-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc20ac1418988b60072d783c9f68e28a173fb63493c127952f6face3b40c6e0" + +[[package]] +name = "reloadable-state" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3853ef78d45b50f8b989896304a85239539d39b7f866a000e8846b9b72d74ce8" +dependencies = [ + "arc-swap", + "reloadable-core", + "tokio", +] + [[package]] name = "reqwest" version = "0.12.22" @@ -2785,6 +3410,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.44" @@ -2826,6 +3460,41 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-cert-file-reader" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f351eaf1dd003022222d2b1399caac198fefeab45c46b0f98bb03fc7cda9bb27" +dependencies = [ + "rustls-cert-read", + "rustls-pemfile", + "rustls-pki-types", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "rustls-cert-read" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd46e8c5ae4de3345c4786a83f99ec7aff287209b9e26fa883c473aeb28f19d5" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-cert-reloadable-resolver" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1baa8a3a1f05eaa9fc55aed4342867f70e5c170ea3bfed1b38c51a4857c0c8" +dependencies = [ + "futures-util", + "reloadable-state", + "rustls", + "rustls-cert-read", + "thiserror 2.0.12", +] + [[package]] name = "rustls-native-certs" version = "0.8.2" @@ -2838,6 +3507,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -2923,7 +3601,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3001,6 +3679,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bencode" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e" +dependencies = [ + "serde", + "serde_bytes", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -3043,6 +3731,26 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3227,6 +3935,15 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -3259,6 +3976,35 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "struct_iterable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849a064c6470a650b72e41fa6c057879b68f804d113af92900f27574828e7712" +dependencies = [ + "struct_iterable_derive", + "struct_iterable_internal", +] + +[[package]] +name = "struct_iterable_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb939ce88a43ea4e9d012f2f6b4cc789deb2db9d47bad697952a85d6978662c" +dependencies = [ + "erased-serde", + "proc-macro2", + "quote", + "struct_iterable_internal", + "syn 2.0.104", +] + +[[package]] +name = "struct_iterable_internal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9426b2a0c03e6cc2ea8dbc0168dbbf943f88755e409fb91bcb8f6a268305f4a" + [[package]] name = "strum" version = "0.27.2" @@ -3365,7 +4111,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.0.7", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3492,11 +4238,12 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2 0.6.0", "tokio-macros", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3520,6 +4267,35 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls-acme" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdba5ab34e36bb015bb2cdfc13a3ee3473bcba240162f96301a3eacef2f769d" +dependencies = [ + "async-trait", + "axum-server", + "base64", + "chrono", + "futures", + "log", + "num-bigint", + "pem", + "proc-macro2", + "rcgen", + "reqwest", + "ring", + "rustls", + "serde", + "serde_json", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-rustls", + "webpki-roots", + "x509-parser", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -3569,12 +4345,36 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "toml" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -3582,10 +4382,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 0.6.11", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tower" version = "0.5.2" @@ -3599,6 +4414,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3617,6 +4433,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3631,6 +4448,22 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower_governor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44de9b94d849d3c46e06a883d72d408c2de6403367b39df2b1c9d9e7b6736fe6" +dependencies = [ + "axum", + "forwarded-header-value", + "governor", + "http 1.3.1", + "pin-project", + "thiserror 2.0.12", + "tower", + "tracing", +] + [[package]] name = "tracing" version = "0.1.41" @@ -3721,6 +4554,15 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttl_cache" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4189890526f0168710b6ee65ceaedf1460c48a14318ceec933cb26baa492096a" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "typenum" version = "1.18.0" @@ -4272,9 +5114,9 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link 0.2.1", ] @@ -4607,6 +5449,24 @@ dependencies = [ "web-sys", ] +[[package]] +name = "x509-parser" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + [[package]] name = "xml-rs" version = "0.8.26" @@ -4622,6 +5482,15 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index a299916..b69c0c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ edition = "2024" [dependencies] anyhow = "1.0.100" -iroh = "0.94" +iroh = "0.94.0" ed25519-dalek = { version = "2.2.0", features = ["rand_core"] } rand = "0.9" tokio-stream = { version = "0.1.15", features = ["sync"] } @@ -25,7 +25,7 @@ tokio = { version = "1.48", features = [ "sync", "rt", ] } -clap = { version = "4.5.48", features = ["derive"] } +clap = { version = "4.5.50", features = ["derive"] } homedir = "0.3.6" whoami = "1.6.1" z32 = "1.3" @@ -33,10 +33,12 @@ runas = "1.2.0" tempfile = "3.23.0" self-runas = "0.1" +iroh-relay = { version = "0.94.0", features=["server"], optional = true } + [target.'cfg(windows)'.dependencies.windows-service] version = "0.8.0" [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.61" +version = "0.61.2" features = [ "Win32_Foundation", "Win32_Security", @@ -50,7 +52,16 @@ features = [ "Win32_System_WindowsProgramming", "Win32_UI_WindowsAndMessaging", ] + [profile.release] opt-level = 3 lto = true -panic = "abort" \ No newline at end of file +panic = "abort" + +[dev-dependencies] +iroh-relay = { version = "0.94.0", features = ["server", "test-utils"] } +iroh-dns-server = { version = "0.94.0" } + +[[example]] +name = "relay_dns_server" +path = "examples/relay_dns_server.rs" diff --git a/README.md b/README.md index 29a92b7..9298b55 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ or use ephemeral keys ``` -Display its Node ID and share it to allow connection +Display its Endpoint ID and share it to allow connection ![Starting server/Installing as service](/media/t-rec_server_service.gif)
@@ -181,7 +181,7 @@ Display its Node ID and share it to allow connection ## Commands ```bash -# Get your Node ID and info +# Get your Endpoint ID and info > iroh-ssh info # Server modes @@ -194,13 +194,13 @@ Display its Node ID and share it to allow connection > iroh-ssh service uninstall # Uninstall service # Client connection -> iroh-ssh user@ # Connect to remote server -> iroh-ssh connect user@ # Explicit connect command, works with all standard ssh params and flags +> iroh-ssh user@ # Connect to remote server +> iroh-ssh connect user@ # Explicit connect command, works with all standard ssh params and flags ``` ## Security Model -- **Node ID access**: Anyone with the Node ID can reach your SSH port +- **Endpoint ID access**: Anyone with the Endpoint ID can reach your SSH port - **SSH authentication**: SSH key file, certificate and password auth are supported - **Persistent keys**: Uses dedicated `.ssh/iroh_ssh_ed25519` keypair - **QUIC encryption**: Transport layer encryption between endpoints diff --git a/README_es.md b/README_es.md index d3571dd..2ab259d 100644 --- a/README_es.md +++ b/README_es.md @@ -194,8 +194,8 @@ Mostrar su ID de nodo y compártalo para permitir la conexión > iroh-ssh service uninstall # Desinstalar servicio # Conexión de cliente -> iroh-ssh user@ # Conectarse a un servidor remoto -> iroh-ssh connect user@ # Comando de conexión explicito, funciona con todos los parametros y banderas ssh estándar +> iroh-ssh user@ # Conectarse a un servidor remoto +> iroh-ssh connect user@ # Comando de conexión explicito, funciona con todos los parametros y banderas ssh estándar ``` ## Modelo de seguridad diff --git a/README_pt.md b/README_pt.md index c4b0a9b..6c09c45 100644 --- a/README_pt.md +++ b/README_pt.md @@ -194,8 +194,8 @@ Exiba seu ID de nó e compartilhe-o para permitir a conexão > iroh-ssh service uninstall # Desinstalar serviço # Conexão do cliente -> iroh-ssh user@ # Conectar-se a um servidor remoto -> iroh-ssh connect user@ # Comando de conexão explícito, funciona com todos os parâmetros e flags ssh padrão +> iroh-ssh user@ # Conectar-se a um servidor remoto +> iroh-ssh connect user@ # Comando de conexão explícito, funciona com todos os parâmetros e flags ssh padrão ``` ## Modelo de segurança diff --git a/examples/relay_dns_server.rs b/examples/relay_dns_server.rs new file mode 100644 index 0000000..de27e48 --- /dev/null +++ b/examples/relay_dns_server.rs @@ -0,0 +1,58 @@ +use std::net::Ipv4Addr; + +use iroh::RelayUrl; +use iroh_relay::{ + RelayQuicConfig, + server::{AccessConfig, CertConfig, QuicConfig, RelayConfig, Server, ServerConfig, TlsConfig}, +}; + +async fn relay_server() -> anyhow::Result { + let (certs, server_config) = iroh_relay::server::testing::self_signed_tls_certs_and_config(); + + let tls = TlsConfig { + cert: CertConfig::<(), ()>::Manual { certs }, + https_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(), + quic_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(), + server_config, + }; + let quic = QuicConfig { + server_config: tls.server_config.clone(), + bind_addr: tls.quic_bind_addr, + }; + let config = ServerConfig { + relay: Some(RelayConfig { + http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(), + tls: Some(tls), + limits: Default::default(), + key_cache_capacity: Some(1024), + access: AccessConfig::Everyone, + }), + quic: Some(quic), + + ..Default::default() + }; + let server = Server::spawn(config).await?; + let url: RelayUrl = format!("https://{}", server.https_addr().expect("configured")) + .parse() + .expect("invalid relay url"); + + let quic = server + .quic_addr() + .map(|addr| RelayQuicConfig { port: addr.port() }); + + println!("Relay server running at {url}"); + if let Some(quic) = &quic { + println!("QUIC enabled on port {}", quic.port); + } + + Ok(server) +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let relay = relay_server().await?; + + tokio::signal::ctrl_c().await?; + relay.shutdown().await?; + Ok(()) +} diff --git a/src/api.rs b/src/api.rs index 1630df8..cf8c00f 100644 --- a/src/api.rs +++ b/src/api.rs @@ -74,17 +74,19 @@ pub mod service { pub async fn server_mode(server_args: ServerArgs, service: bool) -> anyhow::Result<()> { let mut iroh_ssh_builder = IrohSsh::builder() .accept_incoming(true) - .accept_port(server_args.ssh_port); + .accept_port(server_args.ssh_port) + .relays(server_args.iroh.relay_url); if server_args.persist { iroh_ssh_builder = iroh_ssh_builder.dot_ssh_integration(true, service); } + let iroh_ssh = iroh_ssh_builder.build().await?; println!("Connect to this this machine:"); println!( "\n iroh-ssh {}@{}\n", whoami::username(), - iroh_ssh.node_id() + iroh_ssh.endpoint_id() ); if server_args.persist { let distro_home = my_home()?.ok_or_else(|| anyhow::anyhow!("home directory not found"))?; @@ -108,24 +110,33 @@ pub async fn server_mode(server_args: ServerArgs, service: bool) -> anyhow::Resu } pub async fn proxy_mode(proxy_args: ProxyArgs) -> anyhow::Result<()> { - let iroh_ssh = IrohSsh::builder().accept_incoming(false).build().await?; - let endpoint_id = EndpointId::from_str(if proxy_args.node_id.len() == 64 { - &proxy_args.node_id - } else if proxy_args.node_id.len() > 64 { - &proxy_args.node_id[proxy_args.node_id.len() - 64..] + let iroh_ssh = IrohSsh::builder() + .accept_incoming(false) + .relays(proxy_args.iroh.relay_url) + .build() + .await?; + let endpoint_id = EndpointId::from_str(if proxy_args.endpoint_id.len() == 64 { + &proxy_args.endpoint_id + } else if proxy_args.endpoint_id.len() > 64 { + &proxy_args.endpoint_id[proxy_args.endpoint_id.len() - 64..] } else { - return Err(anyhow::anyhow!("invalid node id length")); + return Err(anyhow::anyhow!("invalid endpoint_id length")); })?; iroh_ssh.connect(endpoint_id).await } pub async fn client_mode(connect_args: ConnectArgs) -> anyhow::Result<()> { - let iroh_ssh = IrohSsh::builder().accept_incoming(false).build().await?; + let iroh_ssh = IrohSsh::builder() + .accept_incoming(false) + .relays(connect_args.iroh.relay_url.clone()) + .build() + .await?; let mut ssh_process = iroh_ssh .start_ssh( connect_args.target, connect_args.ssh, connect_args.remote_cmd, + connect_args.iroh, ) .await?; diff --git a/src/cli.rs b/src/cli.rs index 641a564..ec36796 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,9 @@ use std::{ffi::OsString, path::PathBuf}; use clap::{ArgAction, Args, Parser, Subcommand, command}; +use iroh::RelayUrl; -const TARGET_HELP: &str = "Target in the form user@NODE_ID"; +const TARGET_HELP: &str = "Target in the form user@ENDPOINT_ID"; #[derive(Parser, Debug)] #[command(name = "iroh-ssh", about = "ssh without ip")] @@ -16,11 +17,14 @@ pub struct Cli { #[command(flatten)] pub ssh: SshOpts, + #[command(flatten)] + pub iroh: IrohOpts, + #[arg(trailing_var_arg = true)] pub remote_cmd: Option>, } -#[derive(Subcommand,Debug)] +#[derive(Subcommand, Debug)] pub enum Cmd { Connect(ConnectArgs), #[command(hide = true)] @@ -37,10 +41,22 @@ pub enum Cmd { RunService(ServiceArgs), } +#[derive(Args, Clone, Debug)] +pub struct IrohOpts { + #[arg(long, value_name = "URL", + help = "Custom relay URL (repeatable)", + action = ArgAction::Append, + value_parser = parse_relay_url)] + pub relay_url: Vec, +} + #[derive(Args, Clone, Debug)] pub struct ProxyArgs { - #[arg(help = "Proxy node ID")] - pub node_id: String, + #[arg(help = "Proxy Endpoint ID")] + pub endpoint_id: String, + + #[command(flatten)] + pub iroh: IrohOpts, } #[derive(Args, Clone, Debug)] @@ -51,6 +67,9 @@ pub struct ConnectArgs { #[command(flatten)] pub ssh: SshOpts, + #[command(flatten)] + pub iroh: IrohOpts, + #[arg(trailing_var_arg = true)] pub remote_cmd: Vec, } @@ -63,6 +82,9 @@ pub struct ExecArgs { #[command(flatten)] pub ssh: SshOpts, + #[command(flatten)] + pub iroh: IrohOpts, + #[arg(trailing_var_arg = true, required = true)] pub remote_cmd: Vec, } @@ -73,16 +95,17 @@ pub struct SshOpts { short = 'i', long, value_name = "PATH", - help = "Identity file for publickey auth" + help = "Identity file for publickey auth", + value_parser = clap::value_parser!(PathBuf), )] pub identity_file: Option, #[arg(short = 'L', value_name = "LPORT:HOST:RPORT", - help = "Local forward [bind_addr:]lport:host:rport (host can't be node_id yet)", action = ArgAction::Append)] + help = "Local forward [bind_addr:]lport:host:rport (host can't be endpoint_id yet)", action = ArgAction::Append)] pub local_forward: Vec, #[arg(short = 'R', value_name = "RPORT:HOST:LPORT", - help = "Remote forward [bind_addr:]rport:host:lport (host can't be node_id yet)", action = ArgAction::Append)] + help = "Remote forward [bind_addr:]rport:host:lport (host can't be endpoint_id yet)", action = ArgAction::Append)] pub remote_forward: Vec, #[arg( @@ -133,6 +156,9 @@ pub struct ServerArgs { #[arg(short, long, default_value_t = false)] pub persist: bool, + + #[command(flatten)] + pub iroh: IrohOpts, } #[derive(Subcommand, Clone, Debug)] @@ -149,3 +175,9 @@ pub struct ServiceArgs { #[arg(long, default_value = "22")] pub ssh_port: u16, } + +fn parse_relay_url(relay_url: &str) -> Result { + relay_url + .parse::() + .map_err(|e| format!("Invalid relay URL: {e}")) +} diff --git a/src/lib.rs b/src/lib.rs index 529bde5..9ec2b11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ mod service; mod ssh; use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH}; +use iroh::RelayUrl; use iroh::{Endpoint, protocol::Router}; pub mod api; @@ -35,4 +36,5 @@ pub struct Builder { secret_key: [u8; SECRET_KEY_LENGTH], accept_incoming: bool, accept_port: Option, + relay_urls: Option>, } diff --git a/src/main.rs b/src/main.rs index 0d5bdd9..cf6381b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,14 +15,14 @@ async fn main() -> anyhow::Result<()> { ssh: args.ssh, remote_cmd: args.remote_cmd, target: args.target, + iroh: args.iroh, }; api::client_mode(conn_args).await } Some(Cmd::Server(args)) => api::server_mode(args, false).await, Some(Cmd::Service { op }) => { if !self_runas::is_elevated() { - self_runas::admin()?; - return Ok(()) + return self_runas::admin(); } else { match op { ServiceCmd::Install { ssh_port } => api::service::install(ssh_port).await, @@ -43,6 +43,7 @@ async fn main() -> anyhow::Result<()> { ssh: cli.ssh, remote_cmd: cli.remote_cmd.unwrap_or_default(), target: cli.target.unwrap_or_default(), + iroh: cli.iroh, }; api::client_mode(conn_args).await } diff --git a/src/service/windows.rs b/src/service/windows.rs index 2763ca7..a54e0ea 100644 --- a/src/service/windows.rs +++ b/src/service/windows.rs @@ -496,7 +496,7 @@ impl WindowsService { #[cfg(target_os = "windows")] mod service_runtime { use super::WindowsService; - use crate::ServerArgs; + use crate::{IrohOpts, ServerArgs}; use std::{ffi::OsString, io, sync::mpsc, time::Duration}; use tokio::runtime::Builder; use windows_service::{ @@ -582,6 +582,7 @@ mod service_runtime { ServerArgs { ssh_port, persist: true, + iroh: IrohOpts { relay_url: vec![] }, }, true, ) diff --git a/src/service/windows/firewall.rs b/src/service/windows/firewall.rs index c932e71..a35119e 100644 --- a/src/service/windows/firewall.rs +++ b/src/service/windows/firewall.rs @@ -130,4 +130,4 @@ Write-Host 'Removed HTTPS rule' } Ok(()) -} \ No newline at end of file +} diff --git a/src/ssh.rs b/src/ssh.rs index bc9bb27..cf04b9a 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -1,11 +1,13 @@ -use crate::{Builder, Inner, IrohSsh, cli::SshOpts}; +use crate::{Builder, Inner, IrohOpts, IrohSsh, cli::SshOpts}; use std::{ffi::OsString, process::Stdio}; use anyhow::bail; use ed25519_dalek::SECRET_KEY_LENGTH; use homedir::my_home; use iroh::{ - endpoint::Connection, protocol::{ProtocolHandler, Router}, Endpoint, EndpointId, SecretKey + Endpoint, EndpointId, RelayConfig, RelayMap, RelayUrl, SecretKey, + endpoint::Connection, + protocol::{ProtocolHandler, Router}, }; use tokio::{ net::TcpStream, @@ -18,6 +20,7 @@ impl Builder { secret_key: SecretKey::generate(&mut rand::rng()).to_bytes(), accept_incoming: false, accept_port: None, + relay_urls: None, } } @@ -53,22 +56,37 @@ impl Builder { "dot_ssh_integration: Failed to load/create SSH keys: {:#}", e ); - eprintln!( - "Warning: Failed to load/create persistent SSH keys: {e:#}" - ); + eprintln!("Warning: Failed to load/create persistent SSH keys: {e:#}"); eprintln!("Continuing with ephemeral keys..."); } } self } + /// Note: if an empty vector is provided, no relay URLs will be used. + pub fn relays(mut self, relays: Vec) -> Self { + if relays.is_empty() { + return self; + } + tracing::info!("custom_relay: Using custom relay URLs: {relays:?}"); + self.relay_urls = Some(relays); + self + } + pub async fn build(&mut self) -> anyhow::Result { // Iroh setup let secret_key = SecretKey::from_bytes(&self.secret_key); - let endpoint = Endpoint::builder() - .secret_key(secret_key) - .bind() - .await?; + let mut builder = Endpoint::builder().secret_key(secret_key); + + if let Some(relay_urls) = &self.relay_urls { + tracing::info!("build: Using custom relay URLs: {relay_urls:?}"); + let relay_map = relay_urls + .iter() + .map(|url| RelayConfig::from(url.clone())) + .collect::(); + builder = builder.relay_mode(iroh::RelayMode::Custom(relay_map)); + } + let endpoint = builder.bind().await?; let mut iroh_ssh = IrohSsh { public_key: *endpoint.id().as_bytes(), @@ -115,12 +133,21 @@ impl IrohSsh { target: String, ssh_opts: SshOpts, remote_cmd: Vec, + iroh_args: IrohOpts, ) -> anyhow::Result { let c_exe = std::env::current_exe()?; let cmd = &mut Command::new("ssh"); - cmd.arg("-o") - .arg(format!("ProxyCommand={} proxy %h", c_exe.display())); + cmd.arg("-o").arg(format!( + "ProxyCommand={} proxy {} %h", + c_exe.display(), + iroh_args + .relay_url + .iter() + .map(|url| format!("--relay-url {url}",)) + .collect::>() + .join(" ") + )); if let Some(p) = ssh_opts.port { cmd.arg("-p").arg(p.to_string()); @@ -181,7 +208,10 @@ impl IrohSsh { pub async fn connect(&self, endpoint_id: EndpointId) -> anyhow::Result<()> { let inner = self.inner.as_ref().expect("inner not set"); - let conn = inner.endpoint.connect(endpoint_id, &IrohSsh::ALPN()).await?; + let conn = inner + .endpoint + .connect(endpoint_id, &IrohSsh::ALPN()) + .await?; let (mut iroh_send, mut iroh_recv) = conn.open_bi().await?; let (mut local_read, mut local_write) = (tokio::io::stdin(), tokio::io::stdout()); let a_to_b = async move { tokio::io::copy(&mut local_read, &mut iroh_send).await }; @@ -198,12 +228,8 @@ impl IrohSsh { Ok(()) } - pub fn node_id(&self) -> EndpointId { - self.inner - .as_ref() - .expect("inner not set") - .endpoint - .id() + pub fn endpoint_id(&self) -> EndpointId { + self.inner.as_ref().expect("inner not set").endpoint.id() } } diff --git a/tests/relay_urls/Makefile b/tests/relay_urls/Makefile new file mode 100644 index 0000000..4aaa231 --- /dev/null +++ b/tests/relay_urls/Makefile @@ -0,0 +1,17 @@ +.PHONY: setup +setup: + @if [ ! -d certs ]; then \ + mkdir certs; \ + fi; \ + if [ ! -d rcgen ]; then \ + git clone https://github.com/rustls/rcgen; \ + fi; \ + cd rcgen && \ + cargo run -- -o ../certs && \ + cargo install iroh-relay --features=server && \ + cd .. && \ + rm -rf rcgen + +.PHONY: start-relay +start-relay: + iroh-relay --config-path relay.conf --dev \ No newline at end of file diff --git a/tests/relay_urls/relay.conf b/tests/relay_urls/relay.conf new file mode 100644 index 0000000..0eb21fc --- /dev/null +++ b/tests/relay_urls/relay.conf @@ -0,0 +1,6 @@ +enable_quic_addr_discovery = true + +[tls] +cert_mode = "Manual" +manual_cert_path = "certs/cert.pem" +manual_key_path = "certs/cert.key.pem" \ No newline at end of file