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

@@ -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