diff --git a/.gitignore b/.gitignore index c41cc9e..d85f55f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -/target \ No newline at end of file +/target + +# Added by cargo +# +# already existing elements were commented out + +#/target diff --git a/.typos.toml b/.typos.toml deleted file mode 100644 index b115e8b..0000000 --- a/.typos.toml +++ /dev/null @@ -1,3 +0,0 @@ -[default.extend-words] -# Ignore false positives -ratatui = "ratatui" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index dcdebf8..f69c223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,35 +4,35 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.5.2" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "ac8202ab55fcbf46ca829833f347a82a2a4ce0596f0304ac322c2d100030cd56" dependencies = [ "bytes", "crypto-common", - "generic-array", + "inout", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -43,12 +43,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -60,9 +54,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -75,89 +69,56 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - -[[package]] -name = "asn1-rs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", - "time", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] -name = "asn1-rs-derive" -version = "0.5.1" +name = "arrayref" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "synstructure", -] +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] -name = "asn1-rs-impl" -version = "0.2.0" +name = "arrayvec" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-compat" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bab94bde396a3f7b4962e396fdad640e241ed797d4d8d77fc8c237d14c58fc0" +checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590" dependencies = [ "futures-core", "futures-io", @@ -168,13 +129,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -188,6 +149,15 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -196,26 +166,27 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.24.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ - "http 0.2.12", + "base64", + "http", "log", "url", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backon" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ "fastrand", "gloo-timers", @@ -224,9 +195,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -234,14 +205,14 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] name = "base16ct" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" [[package]] name = "base32" @@ -257,62 +228,44 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] -name = "bincode" -version = "2.0.1" +name = "bitflags" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "bincode_derive", - "serde", - "unty", -] +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] -name = "bincode_derive" -version = "2.0.1" +name = "blake3" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ - "virtue", + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" dependencies = [ - "generic-array", + "hybrid-array", + "zeroize", ] -[[package]] -name = "bounded-integer" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102dbef1187b1893e6dfe05a774e79fd52265f49f214f6879c8ff49f52c8188b" - [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -322,24 +275,46 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] [[package]] name = "cc" -version = "1.2.22" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ + "find-msvc-tools", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -349,34 +324,35 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.9.1" +version = "0.10.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +checksum = "9bd162f2b8af3e0639d83f28a637e4e55657b7a74508dba5a9bf4da523d5c9e9" dependencies = [ "cfg-if", "cipher", "cpufeatures", + "zeroize", ] [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "1e12a13eb01ded5d32ee9658d94f553a19e804204f2dc811df69ab4d9e0cb8c7" dependencies = [ + "block-buffer", "crypto-common", "inout", "zeroize", @@ -384,9 +360,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -394,9 +370,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -406,33 +382,36 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] [[package]] name = "color-eyre" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" dependencies = [ "backtrace", "color-spantrace", @@ -445,9 +424,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" dependencies = [ "once_cell", "owo-colors", @@ -457,30 +436,60 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] -name = "concurrent-queue" -version = "2.5.0" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "crossbeam-utils", + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", ] [[package]] name = "const-oid" -version = "0.9.6" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "convert_case" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "cordyceps" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0392f465ceba1713d30708f61c160ebf4dc1cf86bb166039d16b11ad4f3b5b6" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" dependencies = [ "loom", "tracing", @@ -496,6 +505,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -511,21 +530,6 @@ 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" @@ -556,22 +560,46 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.2.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6" dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", + "hybrid-array", + "rand_core 0.9.3", ] [[package]] name = "crypto_box" -version = "0.9.1" +version = "0.10.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16182b4f39a82ec8a6851155cc4c0cda3065bb1db33651726a29e1951de0f009" +checksum = "2bda4de3e070830cf3a27a394de135b6709aefcc54d1e16f2f029271254a6ed9" dependencies = [ "aead", "chacha20", @@ -585,14 +613,14 @@ dependencies = [ [[package]] name = "crypto_secretbox" -version = "0.1.1" +version = "0.2.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" +checksum = "54532aae6546084a52cef855593daf9555945719eeeda9974150e0def854873e" dependencies = [ "aead", "chacha20", "cipher", - "generic-array", + "hybrid-array", "poly1305", "salsa20", "subtle", @@ -601,16 +629,16 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "5.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", - "rand_core 0.6.4", + "rand_core 0.9.3", "rustc_version", "serde", "subtle", @@ -625,7 +653,42 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", ] [[package]] @@ -636,68 +699,65 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "der" -version = "0.7.10" +version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ "const-oid", - "der_derive", "pem-rfc7468", "zeroize", ] [[package]] -name = "der-parser" -version = "9.0.0" +name = "deranged" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", + "powerfmt", ] [[package]] -name = "der_derive" -version = "0.7.3" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "derive_more-impl 1.0.0", ] [[package]] -name = "deranged" -version = "0.4.0" +name = "derive_more" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ - "powerfmt", + "derive_more-impl 2.1.0", ] [[package]] -name = "derive_more" +name = "derive_more-impl" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "derive_more-impl", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "derive_more-impl" -version = "1.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.101", + "rustc_version", + "syn", "unicode-xid", ] @@ -709,13 +769,13 @@ checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" [[package]] name = "digest" -version = "0.10.7" +version = "0.11.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "dac89f8a64533a9b0eaa73a68e424db0fb1fd6271c74cc0125336a05f090568d" dependencies = [ "block-buffer", + "const-oid", "crypto-common", - "subtle", ] [[package]] @@ -736,7 +796,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -747,7 +807,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -763,24 +823,24 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ed25519" -version = "2.2.3" +version = "3.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "594435fe09e345ee388e4e8422072ff7dfeca8729389fbd997b3f5504c44cd47" dependencies = [ "pkcs8", "serde", @@ -789,19 +849,26 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "3.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core 0.6.4", + "rand_core 0.9.3", "serde", "sha2", + "signature", "subtle", "zeroize", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "embedded-io" version = "0.4.0" @@ -823,35 +890,25 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] -name = "enumflags2" -version = "0.7.11" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" -dependencies = [ - "enumflags2_derive", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "enumflags2_derive" -version = "0.7.11" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "libc", + "windows-sys 0.61.2", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "eyre" version = "0.6.12" @@ -862,12 +919,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - [[package]] name = "fastrand" version = "2.3.0" @@ -876,9 +927,15 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fnv" @@ -893,11 +950,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "foldhash" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ "percent-encoding", ] @@ -918,15 +981,15 @@ dependencies = [ [[package]] name = "futures-buffered" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe940397c8b744b9c2c974791c2c08bca2c3242ce0290393249e98f215a00472" +checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" dependencies = [ "cordyceps", "diatomic-waker", "futures-core", "pin-project-lite", - "spin", + "spin 0.10.0", ] [[package]] @@ -964,9 +1027,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -983,7 +1046,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -1018,26 +1081,16 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" dependencies = [ + "cc", "cfg-if", "libc", "log", "rustversion", - "windows 0.58.0", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", + "windows 0.61.3", ] [[package]] @@ -1049,35 +1102,29 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "gloo-timers" @@ -1093,16 +1140,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http", "indexmap", "slab", "tokio", @@ -1110,15 +1157,49 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", ] [[package]] @@ -1140,20 +1221,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", + "bytes", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", + "h2", + "http", "idna", "ipnet", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "ring", - "thiserror 2.0.12", + "rustls", + "thiserror 2.0.17", "tinyvec", "tokio", + "tokio-rustls", "tracing", "url", ] @@ -1171,64 +1257,23 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.1", + "rand 0.9.2", "resolv-conf", + "rustls", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", + "tokio-rustls", "tracing", ] -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "hmac-sha1" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b05da5b9e5d4720bfb691eebb2b9d42da3570745da71eac8a1f5bb7e59aab88" -dependencies = [ - "hmac", - "sha1", -] - -[[package]] -name = "hmac-sha256" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb" - -[[package]] -name = "hostname-validator" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1239,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -1250,7 +1295,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http", "http-body", "pin-project-lite", ] @@ -1267,22 +1312,34 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +dependencies = [ + "typenum", + "zeroize", +] + [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", - "http 1.3.1", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1290,12 +1347,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 1.3.1", + "http", "hyper", "hyper-util", "rustls", @@ -1303,24 +1359,28 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 0.26.11", + "webpki-roots", ] [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.3.1", + "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -1328,9 +1388,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1338,7 +1398,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.62.2", ] [[package]] @@ -1352,9 +1412,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1365,9 +1425,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1378,11 +1438,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1393,42 +1452,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1436,11 +1491,17 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1459,20 +1520,20 @@ dependencies = [ [[package]] name = "igd-next" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06464e726471718db9ad3fefc020529fabcde03313a0fc3967510e2db5add12" +checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" dependencies = [ "async-trait", "attohttpc", "bytes", "futures", - "http 1.3.1", + "http", "http-body-util", "hyper", "hyper-util", "log", - "rand 0.9.1", + "rand 0.9.2", "tokio", "url", "xmltree", @@ -1480,27 +1541,49 @@ dependencies = [ [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", ] [[package]] name = "inout" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "c7357b6e7aa75618c7864ebd0634b115a7218b0615f4cb1df33ac3eca23943d4" dependencies = [ - "generic-array", + "hybrid-array", +] + +[[package]] +name = "instability" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1521,7 +1604,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", @@ -1533,29 +1616,34 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "iroh" -version = "0.35.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ca758f4ce39ae3f07de922be6c73de6a48a07f39554e78b5745585652ce38f5" +checksum = "2374ba3cdaac152dc6ada92d971f7328e6408286faab3b7350842b2ebbed4789" dependencies = [ "aead", - "anyhow", - "atomic-waker", "backon", "bytes", "cfg_aliases", - "concurrent-queue", "crypto_box", "data-encoding", - "der", - "derive_more", + "derive_more 2.1.0", "ed25519-dalek", - "futures-buffered", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.4", "hickory-resolver", - "http 1.3.1", + "http", "igd-next", "instant", "iroh-base", @@ -1564,25 +1652,24 @@ dependencies = [ "iroh-quinn-proto", "iroh-quinn-udp", "iroh-relay", + "n0-error", "n0-future", + "n0-watcher", "netdev", "netwatch", "pin-project", "pkarr", + "pkcs8", "portmapper", - "rand 0.8.5", - "rcgen", + "rand 0.9.2", "reqwest", - "ring", "rustls", - "rustls-webpki 0.102.8", + "rustls-pki-types", + "rustls-platform-verifier", + "rustls-webpki", "serde", "smallvec", - "spki", - "strum", - "stun-rs", - "surge-ping", - "thiserror 2.0.12", + "strum 0.27.2", "time", "tokio", "tokio-stream", @@ -1590,57 +1677,60 @@ dependencies = [ "tracing", "url", "wasm-bindgen-futures", - "webpki-roots 0.26.11", - "x509-parser", + "webpki-roots", "z32", ] [[package]] name = "iroh-base" -version = "0.35.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91ac4aaab68153d726c4e6b39c30f9f9253743f0e25664e52f4caeb46f48d11" +checksum = "25a8c5fb1cc65589f0d7ab44269a76f615a8c4458356952c9b0ef1c93ea45ff8" dependencies = [ "curve25519-dalek", "data-encoding", - "derive_more", + "derive_more 2.1.0", "ed25519-dalek", - "rand_core 0.6.4", + "n0-error", + "rand_core 0.9.3", "serde", - "thiserror 2.0.12", "url", + "zeroize", + "zeroize_derive", ] [[package]] name = "iroh-metrics" -version = "0.34.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f70466f14caff7420a14373676947e25e2917af6a5b1bec45825beb2bf1eb6a7" +checksum = "79e3381da7c93c12d353230c74bba26131d1c8bf3a4d8af0fec041546454582e" dependencies = [ "iroh-metrics-derive", "itoa", + "n0-error", + "postcard", + "ryu", "serde", - "snafu", "tracing", ] [[package]] name = "iroh-metrics-derive" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d12f5c45c4ed2436302a4e03cad9a0ad34b2962ad0c5791e1019c0ee30eeb09" +checksum = "d4e12bd0763fd16062f5cc5e8db15dd52d26e75a8af4c7fb57ccee3589b344b8" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "iroh-quinn" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c6245c9ed906506ab9185e8d7f64857129aee4f935e899f398a3bd3b70338d" +checksum = "0cde160ebee7aabede6ae887460cd303c8b809054224815addf1469d54a6fcf7" dependencies = [ "bytes", "cfg_aliases", @@ -1649,8 +1739,8 @@ dependencies = [ "pin-project-lite", "rustc-hash", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.5.10", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -1670,7 +1760,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -1685,25 +1775,25 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] [[package]] name = "iroh-relay" -version = "0.35.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c63f122cdfaa4b4e0e7d6d3921d2b878f42a0c6d3ee5a29456dc3f5ab5ec931f" +checksum = "43fbdf2aeffa7d6ede1a31f6570866c2199b1cee96a0b563994623795d1bac2c" dependencies = [ - "anyhow", + "blake3", "bytes", "cfg_aliases", "data-encoding", - "derive_more", - "getrandom 0.3.3", + "derive_more 2.1.0", + "getrandom 0.3.4", "hickory-resolver", - "http 1.3.1", + "http", "http-body-util", "hyper", "hyper-util", @@ -1711,37 +1801,46 @@ dependencies = [ "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", - "lru 0.12.5", + "lru 0.16.2", + "n0-error", "n0-future", "num_enum", "pin-project", "pkarr", "postcard", - "rand 0.8.5", + "rand 0.9.2", "reqwest", "rustls", - "rustls-webpki 0.102.8", + "rustls-pki-types", "serde", + "serde_bytes", "sha1", - "strum", - "stun-rs", - "thiserror 2.0.12", + "strum 0.27.2", "tokio", "tokio-rustls", "tokio-util", "tokio-websockets", "tracing", "url", - "webpki-roots 0.26.11", + "webpki-roots", "ws_stream_wasm", "z32", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] name = "itoa" @@ -1749,61 +1848,57 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "lantun-cli" -version = "0.1.1" +name = "lantun" +version = "0.1.0" dependencies = [ "clap", "color-eyre", "dirs", - "futures", "hex", - "lantun-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iroh", + "rand 0.9.2", + "ratatui", "serde", + "thiserror 2.0.17", "tokio", "toml", "tracing", "tracing-subscriber", ] -[[package]] -name = "lantun-core" -version = "0.1.1" -dependencies = [ - "bincode", - "iroh", - "rand 0.8.5", - "serde", - "thiserror 2.0.12", - "tokio", - "tracing", -] - -[[package]] -name = "lantun-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25bd1937a74c0a498654764849b018e05f04ddfdbf73b507cb617c121d6a1d" -dependencies = [ - "bincode", - "iroh", - "rand 0.8.5", - "serde", - "thiserror 2.0.12", - "tokio", - "tracing", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1812,47 +1907,52 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.0", + "bitflags", "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -1873,7 +1973,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", ] [[package]] @@ -1882,6 +1982,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -1890,84 +1999,87 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" +version = "2.7.6" 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" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "log", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "moka" -version = "0.12.10" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "loom", + "equivalent", "parking_lot", "portable-atomic", "rustc_version", "smallvec", "tagptr", - "thiserror 1.0.69", "uuid", ] +[[package]] +name = "n0-error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d5969a2f40e9d9ed121a789c415f4114ac2b28e5731c080bdefee217d3b3fb" +dependencies = [ + "n0-error-macros", + "spez", +] + +[[package]] +name = "n0-error-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6908df844696d9af91c7c3950d50e52d67df327d02a95367f95bbf177d6556" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "n0-future" -version = "0.1.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" +checksum = "8c0709ac8235ce13b82bc4d180ee3c42364b90c1a8a628c3422d991d75a728b5" dependencies = [ "cfg_aliases", - "derive_more", + "derive_more 1.0.0", "futures-buffered", "futures-lite", "futures-util", @@ -1982,98 +2094,66 @@ dependencies = [ ] [[package]] -name = "nested_enum_utils" -version = "0.2.2" +name = "n0-watcher" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa9161ed44d30e9702fe42bd78693bceac0fed02f647da749f36109023d3a3" +checksum = "38acf13c1ddafc60eb7316d52213467f8ccb70b6f02b65e7d97f7799b1f50be4" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "derive_more 2.1.0", + "n0-error", + "n0-future", ] [[package]] name = "netdev" -version = "0.31.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" +checksum = "67ab878b4c90faf36dab10ea51d48c69ae9019bcca47c048a7c9b273d5d7a823" dependencies = [ "dlopen2", "ipnet", "libc", "netlink-packet-core", - "netlink-packet-route 0.17.1", + "netlink-packet-route", "netlink-sys", "once_cell", "system-configuration", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "netlink-packet-core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" -dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-route" -version = "0.17.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" dependencies = [ - "anyhow", - "bitflags 1.3.2", - "byteorder", - "libc", - "netlink-packet-core", - "netlink-packet-utils", + "paste", ] [[package]] name = "netlink-packet-route" -version = "0.23.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0800eae8638a299eaa67476e1c6b6692922273e0f7939fd188fc861c837b9cd2" +checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" dependencies = [ - "anyhow", - "bitflags 2.9.0", - "byteorder", + "bitflags", "libc", "log", "netlink-packet-core", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror 1.0.69", ] [[package]] name = "netlink-proto" -version = "0.11.5" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" +checksum = "b65d130ee111430e47eed7896ea43ca693c387f097dd97376bffafbf25812128" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -2091,53 +2171,38 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.5.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eeaa5f7505c93c5a9b35ba84fd21fb8aa3f24678c76acfe8716af7862fb07a" +checksum = "26f2acd376ef48b6c326abf3ba23c449e0cb8aa5c2511d189dd8a8a3bfac889b" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", - "derive_more", + "derive_more 2.1.0", "iroh-quinn-udp", "js-sys", "libc", + "n0-error", "n0-future", - "nested_enum_utils", + "n0-watcher", "netdev", "netlink-packet-core", - "netlink-packet-route 0.23.0", + "netlink-packet-route", "netlink-proto", "netlink-sys", + "pin-project-lite", "serde", - "snafu", - "socket2", + "socket2 0.6.1", "time", "tokio", "tokio-util", "tracing", "web-sys", - "windows 0.59.0", - "windows-result 0.3.2", + "windows 0.62.2", + "windows-result 0.4.1", "wmi", ] -[[package]] -name = "no-std-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" - -[[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 = "ntimestamp" version = "1.0.0" @@ -2155,22 +2220,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "num-integer", - "num-traits", + "windows-sys 0.61.2", ] [[package]] @@ -2179,15 +2233,6 @@ 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" @@ -2199,43 +2244,35 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] -[[package]] -name = "oid-registry" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" -dependencies = [ - "asn1-rs", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -2247,28 +2284,28 @@ dependencies = [ ] [[package]] -name = "opaque-debug" -version = "0.3.1" +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] -name = "option-ext" -version = "0.2.0" +name = "openssl-probe" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] -name = "overload" -version = "0.1.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "owo-colors" -version = "4.2.0" +version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" [[package]] name = "parking" @@ -2278,9 +2315,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -2288,15 +2325,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -2305,75 +2342,20 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pem" -version = "3.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" -dependencies = [ - "base64", - "serde", -] - [[package]] name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" -dependencies = [ - "memchr", - "thiserror 2.0.12", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.101", +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", ] [[package]] -name = "pest_meta" -version = "2.8.0" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" -dependencies = [ - "once_cell", - "pest", - "sha2", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pharos" @@ -2402,7 +2384,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -2419,9 +2401,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "3.7.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32222ae3d617bf92414db29085f8a959a4515effce916e038e9399a335a0d6d" +checksum = "792c1328860f6874e90e3b387b4929819cc7783a6bd5a4728e918706eb436a48" dependencies = [ "async-compat", "base32", @@ -2432,7 +2414,7 @@ dependencies = [ "ed25519-dalek", "futures-buffered", "futures-lite", - "getrandom 0.2.16", + "getrandom 0.3.4", "log", "lru 0.13.0", "ntimestamp", @@ -2441,7 +2423,7 @@ dependencies = [ "serde", "sha1_smol", "simple-dns", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -2450,96 +2432,52 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.10.2" +version = "0.11.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "77089aec8290d0b7bb01b671b091095cf1937670725af4fd73d47249f03b12c0" dependencies = [ "der", "spki", ] -[[package]] -name = "pnet_base" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" -dependencies = [ - "no-std-net", -] - -[[package]] -name = "pnet_macros" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.101", -] - -[[package]] -name = "pnet_macros_support" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56" -dependencies = [ - "pnet_base", -] - -[[package]] -name = "pnet_packet" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba" -dependencies = [ - "glob", - "pnet_base", - "pnet_macros", - "pnet_macros_support", -] - [[package]] name = "poly1305" -version = "0.8.0" +version = "0.9.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "fb78a635f75d76d856374961deecf61031c0b6f928c83dc9c0924ab6c019c298" dependencies = [ "cpufeatures", - "opaque-debug", "universal-hash", ] [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portmapper" -version = "0.5.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6db66007eac4a0ec8331d0d20c734bd64f6445d64bbaf0d0a27fea7a054e36" +checksum = "7b575f975dcf03e258b0c7ab3f81497d7124f508884c37da66a7314aa2a8d467" dependencies = [ "base64", "bytes", - "derive_more", + "derive_more 2.1.0", "futures-lite", "futures-util", "hyper-util", "igd-next", "iroh-metrics", "libc", - "nested_enum_utils", + "n0-error", "netwatch", "num_enum", - "rand 0.8.5", + "rand 0.9.2", "serde", "smallvec", - "snafu", - "socket2", + "socket2 0.6.1", "time", "tokio", "tokio-util", @@ -2550,33 +2488,34 @@ dependencies = [ [[package]] name = "postcard" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", "embedded-io 0.6.1", + "heapless", "postcard-derive", "serde", ] [[package]] name = "postcard-derive" -version = "0.1.2" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0239fa9c1d225d4b7eb69925c25c5e082307a141e470573fbbe3a817ce6a7a37" +checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2596,63 +2535,29 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precis-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2e7b31f132e0c6f8682cfb7bf4a5340dbe925b7986618d0826a56dfe0c8e56" -dependencies = [ - "precis-tools", - "ucd-parse", - "unicode-normalization", -] - -[[package]] -name = "precis-profiles" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4f67f78f50388f03494794766ba824a704db16fb5d400fe8d545fa7bc0d3f1" -dependencies = [ - "lazy_static", - "precis-core", - "precis-tools", - "unicode-normalization", -] - -[[package]] -name = "precis-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc1eb2d5887ac7bfd2c0b745764db89edb84b856e4214e204ef48ef96d10c4a" -dependencies = [ - "lazy_static", - "regex", - "ucd-parse", -] - [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -2661,8 +2566,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.6.1", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -2670,20 +2575,20 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -2691,42 +2596,32 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] -[[package]] -name = "quoted-string-parser" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc75379cdb451d001f1cb667a9f74e8b355e9df84cc5193513cbe62b96fc5e9" -dependencies = [ - "pest", - "pest_derive", -] - [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -2741,9 +2636,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -2784,118 +2679,89 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] -name = "rcgen" -version = "0.13.2" +name = "ratatui" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "yasna", + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru 0.12.5", + "paste", + "strum 0.26.3", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", ] [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "thiserror 2.0.17", ] [[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", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-lite" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -2905,21 +2771,21 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.11", - "windows-registry", + "webpki-roots", ] [[package]] name = "resolv-conf" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "ring" @@ -2937,9 +2803,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2957,64 +2823,87 @@ dependencies = [ ] [[package]] -name = "rusticata-macros" -version = "4.1.0" +name = "rustix" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "nom", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki", "subtle", "zeroize", ] [[package]] -name = "rustls-pemfile" -version = "2.2.0" +name = "rustls-native-certs" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ + "openssl-probe", "rustls-pki-types", + "schannel", + "security-framework", ] [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", "zeroize", ] [[package]] -name = "rustls-webpki" -version = "0.102.8" +name = "rustls-platform-verifier" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", ] +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -3023,9 +2912,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -3035,13 +2924,32 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa20" -version = "0.10.2" +version = "0.11.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +checksum = "d3ff3b81c8a6e381bc1673768141383f9328048a60edddcfc752a8291a138443" dependencies = [ + "cfg-if", "cipher", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3054,17 +2962,40 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "send_wrapper" @@ -3074,43 +3005,64 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -3127,9 +3079,9 @@ dependencies = [ [[package]] name = "serdect" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2" dependencies = [ "base16ct", "serde", @@ -3137,9 +3089,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "c5e046edf639aa2e7afb285589e5405de2ef7e61d4b0ac1e30256e3eab911af9" dependencies = [ "cfg-if", "cpufeatures", @@ -3154,9 +3106,9 @@ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" dependencies = [ "cfg-if", "cpufeatures", @@ -3179,23 +3131,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "signal-hook-registry" -version = "1.4.5" +name = "signal-hook" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", + "signal-hook-registry", ] [[package]] -name = "signature" -version = "2.2.0" +name = "signal-hook-mio" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ - "rand_core 0.6.4", + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", ] +[[package]] +name = "signature" +version = "3.0.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0251c9d6468f4ba853b6352b190fb7c1e405087779917c238445eb03993826" + [[package]] name = "simdutf8" version = "0.1.5" @@ -3208,66 +3178,72 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "snafu" -version = "0.8.5" +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ - "snafu-derive", + "libc", + "windows-sys 0.60.2", ] [[package]] -name = "snafu-derive" -version = "0.8.5" +name = "spez" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ - "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] -name = "socket2" -version = "0.5.9" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ - "libc", - "windows-sys 0.52.0", + "lock_api", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" [[package]] name = "spki" -version = "0.7.3" +version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" dependencies = [ "base64ct", "der", @@ -3275,9 +3251,15 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" @@ -3291,7 +3273,16 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", ] [[package]] @@ -3304,31 +3295,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn", ] [[package]] -name = "stun-rs" -version = "0.1.11" +name = "strum_macros" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb921f10397d5669e1af6455e9e2d367bf1f9cebcd6b1dd1dc50e19f6a9ac2ac" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "base64", - "bounded-integer", - "byteorder", - "crc", - "enumflags2", - "fallible-iterator", - "hmac-sha1", - "hmac-sha256", - "hostname-validator", - "lazy_static", - "md5", - "paste", - "precis-core", - "precis-profiles", - "quoted-string-parser", - "rand 0.9.1", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -3337,38 +3316,11 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "surge-ping" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fda78103d8016bb25c331ddc54af634e801806463682cc3e549d335df644d95" -dependencies = [ - "hex", - "parking_lot", - "pnet_packet", - "rand 0.9.1", - "socket2", - "thiserror 1.0.69", - "tokio", - "tracing", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" -version = "2.0.101" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3392,7 +3344,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -3401,8 +3353,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", - "core-foundation", + "bitflags", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3433,11 +3385,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -3448,67 +3400,54 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", - "itoa", "js-sys", "num-conv", "powerfmt", "serde", "time-core", - "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "time-macros" -version = "0.2.22" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" -dependencies = [ - "num-conv", - "time-core", -] +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -3516,9 +3455,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -3531,37 +3470,36 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.0" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -3581,33 +3519,32 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", "futures-sink", "futures-util", - "hashbrown", "pin-project-lite", "tokio", ] [[package]] name = "tokio-websockets" -version = "0.11.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fcaf159b4e7a376b05b5bfd77bfd38f3324f5fce751b4213bfc7eaa47affb4e" +checksum = "b1b6348ebfaaecd771cecb69e832961d277f59845d4220a584701f72728152b7" dependencies = [ "base64", "bytes", "futures-core", "futures-sink", - "getrandom 0.3.3", - "http 1.3.1", + "getrandom 0.3.4", + "http", "httparse", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustls-pki-types", "simdutf8", @@ -3618,44 +3555,54 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", - "toml_write", + "toml_parser", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.1" +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -3672,6 +3619,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -3686,9 +3651,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -3698,20 +3663,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -3740,14 +3705,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -3764,39 +3729,44 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] -name = "ucd-parse" -version = "0.1.13" +name = "unicode-ident" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06ff81122fcbf4df4c1660b15f7e3336058e7aec14437c9f85c6b31a0f279b9" -dependencies = [ - "regex-lite", -] +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] -name = "ucd-trie" -version = "0.1.7" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "unicode-ident" -version = "1.0.18" +name = "unicode-truncate" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] [[package]] -name = "unicode-normalization" -version = "0.1.24" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode-xid" @@ -3806,9 +3776,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "a55be643b40a21558f44806b53ee9319595bc7ca6896372e4e08e5d7d83c9cd6" dependencies = [ "crypto-common", "subtle", @@ -3820,17 +3790,11 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -3852,11 +3816,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -3866,16 +3832,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtue" -version = "0.0.18" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "want" @@ -3888,50 +3852,37 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -3942,9 +3893,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3952,22 +3903,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.101", - "wasm-bindgen-backend", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -3987,9 +3938,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -4006,28 +3957,37 @@ dependencies = [ ] [[package]] -name = "webpki-roots" +name = "webpki-root-certs" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.4", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" dependencies = [ - "webpki-roots 1.0.0", + "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -4045,6 +4005,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4053,179 +4022,192 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.58.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", ] [[package]] name = "windows" -version = "0.59.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] -name = "windows-core" -version = "0.58.0" +name = "windows-collections" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core 0.61.2", ] [[package]] -name = "windows-core" -version = "0.59.0" +name = "windows-collections" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement 0.59.0", - "windows-interface 0.59.1", - "windows-result 0.3.2", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core 0.62.2", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] -name = "windows-implement" -version = "0.58.0" +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] -name = "windows-implement" -version = "0.59.0" +name = "windows-future" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", ] [[package]] -name = "windows-implement" -version = "0.60.0" +name = "windows-future" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] -name = "windows-interface" -version = "0.58.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-registry" -version = "0.4.0" +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-result 0.3.2", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core 0.62.2", + "windows-link 0.2.1", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.1.3", ] [[package]] name = "windows-result" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] -name = "windows-strings" -version = "0.4.0" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-link", + "windows-targets 0.42.2", ] [[package]] @@ -4255,6 +4237,39 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4288,20 +4303,45 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4316,9 +4356,15 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -4334,9 +4380,15 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -4352,9 +4404,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -4364,9 +4416,15 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -4382,9 +4440,15 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -4400,9 +4464,15 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -4418,9 +4488,15 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -4436,15 +4512,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -4460,40 +4536,37 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wmi" -version = "0.14.5" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7787dacdd8e71cbc104658aade4009300777f9b5fda6a75f19145fedb8a18e71" +checksum = "120d8c2b6a7c96c27bf4a7947fd7f02d73ca7f5958b8bd72a696e46cb5521ee6" dependencies = [ "chrono", "futures", "log", "serde", - "thiserror 2.0.12", - "windows 0.59.0", - "windows-core 0.59.0", + "thiserror 2.0.17", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "ws_stream_wasm" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" dependencies = [ "async_io_stream", "futures", @@ -4502,34 +4575,17 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 1.0.69", + "thiserror 2.0.17", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] -[[package]] -name = "x509-parser" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmltree" @@ -4540,22 +4596,12 @@ 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" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4563,13 +4609,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", "synstructure", ] @@ -4581,22 +4627,22 @@ checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -4616,21 +4662,35 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -4639,9 +4699,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -4650,11 +4710,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index a2de703..2898bd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,11 @@ -[workspace] -resolver = "1" -members = [ - "lantun-cli", - "lantun-core", -] +[package] +name = "lantun" +version = "0.1.0" +edition = "2024" -[workspace.dependencies] -tokio = { version = "1.45.0", features = [ +[dependencies] +iroh = "0.95.1" +tokio = { version = "1.48.0", features = [ "rt", "rt-multi-thread", "time", @@ -16,10 +15,11 @@ tokio = { version = "1.45.0", features = [ tracing = "0.1.41" tracing-subscriber = "0.3.19" serde = { version = "1.0.219", features = ["derive"] } - -[profile.release] -lto = true -codegen-units = 1 -opt-level = "z" -panic = "abort" -strip = true \ No newline at end of file +hex = "0.4.3" +dirs = "6.0.0" +color-eyre = "0.6.5" +clap = { version = "4.5.53", features = ["derive", "string"] } +ratatui = "0.29.0" +thiserror = "2.0.17" +toml = "0.9.8" +rand = "0.9.2" diff --git a/lantun-cli/Cargo.toml b/lantun-cli/Cargo.toml deleted file mode 100644 index fd0d1ca..0000000 --- a/lantun-cli/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "lantun-cli" -version = "0.1.1" -edition = "2024" -authors = ["Maxomatic458"] -description = "TODO" -repository = "https://github.com/maxomatic458/lantun" -readme = "../README.md" -license = "MIT" -keywords = ["quic", "port-forwarding", "tunneling", "peer-to-peer", "cli"] - -[dependencies] -tracing = { workspace = true } -tracing-subscriber = { workspace = true } -serde = { workspace = true } -tokio = { workspace = true } -clap = { version = "4.5.38", features = ["derive", "string"] } -color-eyre = "0.6.4" -dirs = "6.0.0" -futures = "0.3.31" -hex = "0.4.3" -lantun-core = "0.1.1" -# lantun-core = { path = "../lantun-core" } -toml = "0.8.22" - -[[bin]] -path = "src/main.rs" -name = "lantun" \ No newline at end of file diff --git a/lantun-cli/src/config.rs b/lantun-cli/src/config.rs deleted file mode 100644 index dddef4a..0000000 --- a/lantun-cli/src/config.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{net::SocketAddr, path::Path}; - -use lantun_core::{LanTun, TunnelProtocol}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct LanTunConfig { - pub host_tunnels: Vec, - pub client_tunnels: Vec, -} - -impl LanTunConfig { - pub async fn create_state(&self) -> color_eyre::Result { - let mut lantun = LanTun::default(); - - for tunnel in &self.host_tunnels { - let secret: [u8; 32] = hex::decode(&tunnel.private_secret)? - .try_into() - .map_err(|_| color_eyre::Report::msg("Invalid private secret length"))?; - lantun - .host - .create_tunnel(tunnel.local, tunnel.name.clone(), secret, tunnel.protocol) - .await; - } - - for tunnel in &self.client_tunnels { - let secret: [u8; 32] = hex::decode(&tunnel.secret)? - .try_into() - .map_err(|_| color_eyre::Report::msg("Invalid public secret length"))?; - lantun - .client - .create_tunnel(tunnel.local, tunnel.name.clone(), secret, tunnel.protocol) - .await?; - } - - Ok(lantun) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HostTunnelEntry { - pub name: String, - pub local: SocketAddr, - pub protocol: TunnelProtocol, - pub private_secret: String, - /// This is the secret that the client will use to connect to the tunnel. - pub public_secret: String, - pub enabled: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ClientTunnelEntry { - pub name: String, - pub local: SocketAddr, - pub protocol: TunnelProtocol, - /// The public secret of the host tunnel - pub secret: String, - pub enabled: bool, -} - -pub fn load_config(path: &Path) -> color_eyre::Result { - if !path.exists() { - tracing::debug!("Config file not found, creating a new one at {:?}", path); - std::fs::create_dir_all(path.parent().unwrap())?; - std::fs::write(path, toml::ser::to_string(&LanTunConfig::default())?)?; - return Ok(LanTunConfig::default()); - } - - let config: LanTunConfig = toml::de::from_str(&std::fs::read_to_string(path)?)?; - Ok(config) -} - -pub fn save_config(path: &Path, config: &LanTunConfig) -> color_eyre::Result<()> { - std::fs::write(path, toml::ser::to_string(config)?)?; - tracing::debug!("Config file saved to {:?}", path); - Ok(()) -} diff --git a/lantun-cli/src/main.rs b/lantun-cli/src/main.rs deleted file mode 100644 index 221c722..0000000 --- a/lantun-cli/src/main.rs +++ /dev/null @@ -1,244 +0,0 @@ -use clap::{Parser, Subcommand}; -use config::load_config; -use futures::FutureExt; -use lantun_core::{LANTUN_VERSION, TunnelProtocol, gen_secret}; -use std::{net::SocketAddr, path::PathBuf, pin::Pin}; - -mod config; - -fn default_config_file() -> PathBuf { - dirs::config_dir() - .expect("Failed to get config directory") - .join("lantun") - .join("lantun.toml") -} - -#[derive(Parser, Debug)] -#[clap(version = LANTUN_VERSION, author = env!("CARGO_PKG_AUTHORS"))] -struct Args { - #[clap(long, short)] - /// Log Level - log_level: Option, - /// Config file - #[clap(long, short, default_value = default_config_file().to_string_lossy().to_string())] - config: PathBuf, - /// Action to perform - #[clap(subcommand)] - action: Option, -} - -#[derive(Subcommand, Debug)] -enum Action { - /// Create a new host tunnel - #[clap(name = "add-host")] - CreateHostTunnel { - /// The local address of the tunnel - local: SocketAddr, - /// the protocol of the tunnel - protocol: TunnelProtocol, - /// The name of the tunnel - #[clap(default_value = "host tunnel")] - name: String, - }, - #[clap(name = "add-client")] - AddClientTunnel { - /// The secret of the tunnel - secret: String, - /// The local address this tunnel will bind to - local: SocketAddr, - /// The protocol of the tunnel - protocol: TunnelProtocol, - /// The name of the tunnel - #[clap(default_value = "client tunnel")] - name: String, - }, - /// List all tunnels - #[clap(name = "list")] - ListTunnels, - /// Remove a tunnel - #[clap(name = "remove")] - RemoveTunnel { - /// The secret of the tunnel - name: String, - }, -} - -#[tokio::main] -async fn main() -> color_eyre::Result<()> { - let args = Args::parse(); - - match args.log_level { - None => { - let env_filter = std::env::var("RUST_LOG") - .unwrap_or_else(|_| "lantun_core=info,lantun_cli=info".to_string()); - tracing_subscriber::fmt() - .with_env_filter(env_filter) - .with_target(false) - .without_time() - .init(); - } - Some(level) => { - tracing_subscriber::fmt() - .with_max_level(level) - .with_target(true) - .init(); - } - } - - tracing::debug!("LanTun version: {}", LANTUN_VERSION); - tracing::debug!("Config file: {}", args.config.display()); - - let mut config = load_config(&args.config)?; - - match args.action { - None => { - if config.host_tunnels.is_empty() && config.client_tunnels.is_empty() { - println!( - "No tunnels found. Use `lantun add-host` or `lantun add-client` to create a tunnel." - ); - return Ok(()); - } - - let mut lantun_state = config.create_state().await?; - - #[allow(clippy::type_complexity)] - let mut handles: Vec< - Pin> + Send>>, - > = Vec::with_capacity( - lantun_state.host.tunnels().len() + lantun_state.client.tunnels().len(), - ); - - for host_tunnel in lantun_state.host.tunnels_mut().values_mut() { - let handle = host_tunnel.start().await?; - let fut = handle.map(|res| { - res.map_err(|e| color_eyre::Report::msg(e.to_string())) - .and_then(|inner| inner.map_err(|e| color_eyre::Report::msg(e.to_string()))) - }); - handles.push(Box::pin(fut)); - } - - for client_tunnel in lantun_state.client.tunnels_mut().values_mut() { - let handle = client_tunnel.connect().await?; - let fut = handle.map(|res| { - res.map_err(|e| color_eyre::Report::msg(e.to_string())) - .and_then(|inner| inner.map_err(|e| color_eyre::Report::msg(e.to_string()))) - }); - handles.push(Box::pin(fut)); - } - - futures::future::try_join_all(handles).await?; - } - Some(action) => match action { - Action::CreateHostTunnel { - local, - protocol, - name, - } => { - let secret = gen_secret(); - let private_secret = hex::encode(secret.to_bytes()); - let public_secret = hex::encode(secret.public()); - - let new_tunnel = config::HostTunnelEntry { - name: name.clone(), - local, - protocol, - private_secret, - public_secret: public_secret.clone(), - enabled: true, - }; - - if config - .host_tunnels - .iter() - .any(|t| t.local == local && t.protocol == protocol) - { - tracing::error!("Tunnel with this local address and protocol already exists"); - return Ok(()); - } - - config.host_tunnels.push(new_tunnel); - config.host_tunnels.sort_by(|a, b| a.name.cmp(&b.name)); - - config::save_config(&args.config, &config)?; - - println!( - "Tunnel \"{}\" with local addr {}/{} created.", - name, local, protocol - ); - println!("The public secret is \"{}\"\n", public_secret); - println!("To add a client tunnel on the client side, use:"); - println!( - " lantun add-client {} {} {} ", - public_secret, local, protocol - ); - } - Action::AddClientTunnel { - secret, - local, - protocol, - name, - } => { - let secret = hex::decode(secret).map_err(|_| { - color_eyre::Report::msg("Invalid secret format. It should be a hex string.") - })?; - - let new_tunnel = config::ClientTunnelEntry { - name: name.clone(), - local, - protocol, - secret: hex::encode(secret), - enabled: true, - }; - - if config - .client_tunnels - .iter() - .any(|t| t.local == local && t.protocol == protocol) - { - tracing::error!("Tunnel with this local address and protocol already exists"); - return Ok(()); - } - - config.client_tunnels.push(new_tunnel); - config.client_tunnels.sort_by(|a, b| a.name.cmp(&b.name)); - - config::save_config(&args.config, &config)?; - - println!( - "Client tunnel \"{}\" with local addr {}/{} created.", - name, local, protocol - ); - } - Action::ListTunnels => { - println!("Host Tunnels:"); - for tunnel in &config.host_tunnels { - println!( - " {}: {}/{}: {}", - tunnel.name, tunnel.local, tunnel.protocol, tunnel.public_secret - ); - } - - println!("\nClient Tunnels:"); - for tunnel in &config.client_tunnels { - println!(" {}: {} -> {}", tunnel.name, tunnel.local, tunnel.secret); - } - } - Action::RemoveTunnel { name } => { - if let Some(pos) = config.host_tunnels.iter().position(|t| t.name == name) { - config.host_tunnels.remove(pos); - println!("Removed host tunnel \"{}\"", name); - config::save_config(&args.config, &config)?; - } else if let Some(pos) = config.client_tunnels.iter().position(|t| t.name == name) - { - config.client_tunnels.remove(pos); - println!("Removed client tunnel \"{}\"", name); - config::save_config(&args.config, &config)?; - } else { - println!("Tunnel \"{}\" not found", name); - } - } - }, - } - - Ok(()) -} diff --git a/lantun-core/Cargo.toml b/lantun-core/Cargo.toml deleted file mode 100644 index 7b72e05..0000000 --- a/lantun-core/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "lantun-core" -version = "0.1.1" -edition = "2024" -authors = ["Maxomatic458"] -description = "lantun core library" -repository = "https://github.com/maxomatic458/lantun" -readme = "../README.md" -license = "MIT" -keywords = ["quic", "port-forwarding", "tunneling", "peer-to-peer"] - -[dependencies] -tracing = { workspace = true } -tokio = { workspace = true } -serde = { workspace = true } -iroh = "0.35.0" -bincode = { version = "2.0.1", features = ["serde"] } -thiserror = "2.0.12" -rand = "0.8.5" diff --git a/lantun-core/src/client.rs b/lantun-core/src/client.rs deleted file mode 100644 index 6e4dd00..0000000 --- a/lantun-core/src/client.rs +++ /dev/null @@ -1,390 +0,0 @@ -use std::{ - collections::HashMap, - net::{Ipv4Addr, SocketAddr, SocketAddrV4}, - sync::{ - Arc, - atomic::{AtomicBool, AtomicU64, Ordering}, - }, -}; - -use iroh::{ - Endpoint, PublicKey, RelayMode, - endpoint::{Connection, ReadError, VarInt, WriteError}, -}; -use thiserror::Error; -use tokio::{ - net::{TcpListener, UdpSocket}, - sync::{RwLock, watch}, - task::JoinHandle, -}; - -use crate::{ - ALPN, TunnelProtocol, - common::{Client2HostControlMsg, LocalClientConnection, TunnelCommon, send_packet}, - net::{tcp::bi_tcp_stream_client, udp::bi_udp_client}, -}; - -#[derive(Error, Debug)] -pub enum ClientError { - #[error("Failed to create endpoint: {0}")] - FailedToCreateEndpoint(String), - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - #[error("Write error: {0}")] - WriteError(#[from] WriteError), - #[error("Read error: {0}")] - ReadError(#[from] ReadError), - #[error("Tunnel with local address {0}/{1} already exists")] - TunnelAlreadyExists(SocketAddr, TunnelProtocol), - #[error("Failed to connect: {0}")] - FailedToConnect(String), - #[error("Failed to accept stream: {0}")] - FailedToAcceptStream(String), - #[error("Connection error: {0}")] - ConnectionError(#[from] iroh::endpoint::ConnectionError), - #[error("Already connected to tunnel")] - AlreadyConnected, - #[error("Not connected to tunnel")] - NotConnected, -} - -/// The client state. -pub struct ClientState { - /// There should only be one tunnel per local SocketAddr and proto. - tunnels: HashMap<(SocketAddr, TunnelProtocol), ClientTunnel>, -} - -impl Default for ClientState { - fn default() -> Self { - Self::new() - } -} - -impl ClientState { - /// Create a new client state. - pub fn new() -> Self { - Self { - tunnels: HashMap::new(), - } - } - - /// Get the tunnels. - pub fn tunnels(&self) -> &HashMap<(SocketAddr, TunnelProtocol), ClientTunnel> { - &self.tunnels - } - - /// Get the tunnels mutable. - pub fn tunnels_mut(&mut self) -> &mut HashMap<(SocketAddr, TunnelProtocol), ClientTunnel> { - &mut self.tunnels - } - - /// Create a new tunnel. - pub async fn create_tunnel( - &mut self, - local_addr: SocketAddr, - name: String, - secret: [u8; 32], - protocol: TunnelProtocol, - ) -> Result<&mut ClientTunnel, ClientError> { - if self.tunnels.contains_key(&(local_addr, protocol)) { - return Err(ClientError::TunnelAlreadyExists(local_addr, protocol)); - } - - let tunnel = ClientTunnel { - name, - secret: PublicKey::from_bytes(&secret).unwrap(), - tunnel_addr: local_addr, - protocol, - is_connected: Arc::new(AtomicBool::new(false)), - running_props: None, - }; - - self.tunnels.insert((local_addr, protocol), tunnel); - - Ok(self.tunnels.get_mut(&(local_addr, protocol)).unwrap()) - } - - pub fn get_tunnel( - &self, - local_addr: SocketAddr, - protocol: TunnelProtocol, - ) -> Option<&ClientTunnel> { - self.tunnels.get(&(local_addr, protocol)) - } - - pub fn get_tunnel_mut( - &mut self, - local_addr: SocketAddr, - protocol: TunnelProtocol, - ) -> Option<&mut ClientTunnel> { - self.tunnels.get_mut(&(local_addr, protocol)) - } -} - -/// A [`ClientTunnel`] represents the other end of the [`HostTunnel`]. -pub struct ClientTunnel { - /// The name of the tunnel. - pub name: String, - /// The secret of the tunnel this client is connected to. - /// This value is unique for every existing HostTunnel. - secret: PublicKey, - /// The local address which the traffic of the host's tunnel will be forwarded to. - /// e.g if the Host runs a server, this is the address that the client needs to connect to. - tunnel_addr: SocketAddr, - /// The protocol of the tunnel. - /// TODO: make sure this is the same as the host tunnel. - protocol: TunnelProtocol, - /// If the client tunnel is connected to the host tunnel. - is_connected: Arc, - /// Running tunnel values. - running_props: Option, -} - -/// The properties of the running client tunnel. -struct RunningClientTunnelProps { - /// Local connections of this client. (e.g a game client) - /// - /// The key is the local address of the application connecting to the tunnel socket. - local_connections: Arc>>>>, - /// This tunnels endpoint on the iroh network. - endpoint: Endpoint, - /// The connection to the host tunnel. - connection: Connection, - /// Channel sender to stop the connection. - stop_tx: watch::Sender, -} - -impl ClientTunnel { - /// Get the client address. - pub fn client_addr(&self) -> SocketAddr { - self.tunnel_addr - } - - /// Start the tunnel loop. - pub async fn connect(&mut self) -> Result>, ClientError> { - if self.is_running() { - tracing::warn!( - "Client tunnel \"{}\" is already connected, not starting again", - self.name - ); - return Err(ClientError::AlreadyConnected); - } - - let endpoint = Endpoint::builder() - // .alpns(vec![ALPN.to_vec()]) - .relay_mode(RelayMode::Default) - .discovery_n0() - .bind() - .await - .map_err(|e| ClientError::FailedToCreateEndpoint(e.to_string()))?; - - let conn = endpoint - .connect(self.secret, ALPN) - .await - .map_err(|e| ClientError::FailedToConnect(e.to_string()))?; - - let local_connections = Arc::new(RwLock::new(HashMap::new())); - - let (stop_tx, mut stop_rx) = watch::channel(false); - - self.running_props = Some(RunningClientTunnelProps { - local_connections: local_connections.clone(), - connection: conn.clone(), - stop_tx, - endpoint, - }); - self.is_connected.store(true, Ordering::SeqCst); - - tracing::debug!("Client tunnel \"{}\" connected to host", self.name); - - let protocol = self.protocol; - let tunnel_addr = self.tunnel_addr; - - // TODO: create helper function for this - - let connection_listener = tokio::spawn(async move { - match protocol { - TunnelProtocol::Tcp => { - let sock = TcpListener::bind(tunnel_addr).await?; - tracing::debug!("Client TCP socket listening on {}", tunnel_addr); - - loop { - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping TCP stream for client tunnel"); - break; - } - } - Ok((stream, client_local_addr)) = sock.accept() => { - let (tunnel_send, tunnel_recv) = conn.open_bi().await?; - send_packet(&conn, Client2HostControlMsg::ConnReq { local_addr: client_local_addr }).await?; - - let local_client_connection = LocalClientConnection { - client_local_addr, - // We dont need to know this on the client side. - // Maybe create a separate struct for this? or make it an option? - client_virtual_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), - last_active: Arc::new(AtomicU64::new(0)), - }; - - let local_client_connection = Arc::new(RwLock::new(local_client_connection)); - - { - local_connections.write().await.insert(client_local_addr, local_client_connection.clone()); - } // drop lock - - // Start TCP forwarder for this connection - bi_tcp_stream_client(stream, local_connections.clone(), local_client_connection.clone(), tunnel_send, tunnel_recv, stop_rx.clone()).await?; - } - } - } - } - TunnelProtocol::Udp => { - // This socket is used to establish UDP sessions. - - // We use multiple sockets on the same addr so we can use the - // connected api for ease of use. - // not sure if there are any downsides to this. - let socket = UdpSocket::bind(tunnel_addr).await?; - let socket = Arc::new(socket); - - tracing::debug!("Client UDP socket listening on {}", tunnel_addr); - - loop { - let mut buf = vec![0; 1024]; - let socket = socket.clone(); - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping UDP stream for client tunnel"); - break; - } - } - Ok((_, client_local_addr)) = socket.peek_from(&mut buf) => { - println!("Received UDP packet from {}", client_local_addr); - // If we already have a connection for this local address, skip it - { - let local_connections = local_connections.read().await; - if local_connections.contains_key(&client_local_addr) { - // The connection is already being tunneled - continue; - } - } // drop lock - - let (tunnel_send, tunnel_recv) = conn.open_bi().await?; - send_packet(&conn, Client2HostControlMsg::ConnReq { local_addr: client_local_addr }).await?; - - let local_client_connection = LocalClientConnection { - client_local_addr, - // We dont need to know this on the client side. - // Maybe create a separate struct for this? or make it an option? - client_virtual_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), - last_active: Arc::new(AtomicU64::new(0)), - }; - - let local_client_connection = Arc::new(RwLock::new(local_client_connection)); - { - local_connections.write().await.insert(client_local_addr, local_client_connection.clone()); - } // drop lock - - bi_udp_client(socket, local_connections.clone(), local_client_connection.clone(), tunnel_send, tunnel_recv, stop_rx.clone()).await?; - - } - } - } - } - }; - Ok::<(), ClientError>(()) - }); - - tracing::info!("Client tunnel \"{}\" is connected", self.name); - - Ok(connection_listener) - } - - /// Static method to disconnect a local connection, which closes all related - /// streams and sockets. - /// This is static so it can be called from other threads given the connections. - /// - /// # Returns - /// * `true` if the connection was successfully disconnected - /// * `false` otherwise. - pub async fn disconnect_local_connection( - local_connections: Arc>>>>, - connection_to_disconnect: Arc>, - ) -> bool { - let mut local_connections = local_connections.write().await; - let connection_to_disconnect = connection_to_disconnect.read().await; - - let client_local_addr = connection_to_disconnect.client_local_addr; - - if local_connections.remove(&client_local_addr).is_some() { - tracing::debug!( - "Disconnected local connection {}. Connections left: {}", - client_local_addr, - local_connections.len() - ); - true - } else { - tracing::warn!("Tried disconnecting a local connection that does not exist"); - false - } - } - - /// Terminates all connections and stops the client tunnel. - pub async fn disconnect(&mut self) -> Result<(), ClientError> { - if !self.is_running() { - tracing::warn!( - "Client tunnel \"{}\" is not connected, not stopping", - self.name - ); - return Err(ClientError::NotConnected); - } - - if let Some(running_props) = self.running_props.take() { - running_props.stop_tx.send(true).unwrap(); - running_props.connection.close(VarInt::from_u32(0), &[]); - running_props.endpoint.close().await; - } - - self.is_connected.store(false, Ordering::SeqCst); - - tracing::info!("Client tunnel \"{}\" disconnected", self.name); - - Ok(()) - } -} - -impl TunnelCommon for ClientTunnel { - fn secret(&self) -> [u8; 32] { - *self.secret.as_bytes() - } - - fn protocol(&self) -> TunnelProtocol { - self.protocol - } - - fn is_running(&self) -> bool { - self.is_connected.load(Ordering::SeqCst) - } - - fn name(&self) -> String { - self.name.clone() - } - - async fn num_active_connections(&self) -> usize { - if let Some(running_props) = &self.running_props { - let local_connections = running_props.local_connections.read().await; - local_connections.len() - } else { - 0 - } - } - - fn addr(&self) -> SocketAddr { - self.tunnel_addr - } -} diff --git a/lantun-core/src/common.rs b/lantun-core/src/common.rs deleted file mode 100644 index 523e08c..0000000 --- a/lantun-core/src/common.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::{ - fmt::Debug, - net::SocketAddr, - sync::{Arc, atomic::AtomicU64}, -}; - -use bincode::{Decode, Encode}; -use iroh::endpoint::{Connection, ConnectionError, ReadError}; -use thiserror::Error; -use tokio::io::AsyncWriteExt; - -use crate::TunnelProtocol; - -pub const TUNNEL_FORWARDER_BUFFER_SIZE: usize = 1024; -pub const CONNECTION_TIMEOUT_SECONDS: u64 = 20; - -/// A single local connection of that client. (e.g game client) -pub struct LocalClientConnection { - /// The local address of the client (inside the client network). - pub client_local_addr: SocketAddr, - /// The address of the "virtual client socket" that will be bound on the host machine. - pub client_virtual_addr: SocketAddr, - pub last_active: Arc, -} - -pub async fn send_packet(conn: &Connection, packet: P) -> std::io::Result<()> { - tracing::debug!("Sending packet: {:?}", packet); - let mut send = conn.open_uni().await?; - - let data = bincode::encode_to_vec(&packet, bincode::config::standard()).unwrap(); - send.write_all(&data).await?; - - send.flush().await?; - send.finish()?; - - Ok(()) -} - -#[derive(Error, Debug)] -pub enum ReceivePacketError { - #[error("io error: {0}")] - Io(#[from] std::io::Error), - #[error("decode error: {0}")] - Decode(#[from] bincode::error::DecodeError), - #[error("connection error: {0}")] - Connection(#[from] ConnectionError), - #[error("read error: {0}")] - Read(#[from] ReadError), -} - -pub async fn receive_packet + Debug>( - conn: &Connection, -) -> Result { - let mut recv = conn.accept_uni().await?; - - let mut buf = Vec::new(); - - loop { - let mut data = vec![0; 1024]; - if let Some(n) = recv.read(&mut data).await? { - buf.extend_from_slice(&data[..n]); - continue; - } - - break; - } - - let packet = bincode::decode_from_slice(&buf, bincode::config::standard())?.0; - tracing::debug!("Received packet: {:?}", packet); - - Ok(packet) -} - -#[derive(Encode, Decode, Debug)] -pub enum Client2HostControlMsg { - /// The initial connection request from the client. - ConnReq { - /// The local address of the connection (on the client side). - local_addr: SocketAddr, - }, -} - -pub trait TunnelCommon { - /// Get the tunnel secret. - fn secret(&self) -> [u8; 32]; - /// Get the tunnel protocol. - fn protocol(&self) -> TunnelProtocol; - /// If the tunnel is currently running and connected. - /// For Host tunnels this means clients can connect. - /// For Client tunnels this means the tunnel is connected to a host. - fn is_running(&self) -> bool; - /// Get the tunnel name. - fn name(&self) -> String; - /// Get the amount of active connections. - #[allow(async_fn_in_trait)] - async fn num_active_connections(&self) -> usize; - /// Get the tunnel address. - fn addr(&self) -> SocketAddr; -} diff --git a/lantun-core/src/host.rs b/lantun-core/src/host.rs deleted file mode 100644 index bfaa150..0000000 --- a/lantun-core/src/host.rs +++ /dev/null @@ -1,445 +0,0 @@ -use iroh::{ - Endpoint, PublicKey, RelayMode, SecretKey, - endpoint::{Connection, ReadError, WriteError}, -}; -use std::{ - collections::HashMap, - net::SocketAddr, - sync::{ - Arc, - atomic::{AtomicBool, AtomicU64, Ordering}, - }, -}; -use thiserror::Error; -use tokio::{ - net::{TcpSocket, UdpSocket}, - sync::{RwLock, watch}, - task::JoinHandle, -}; - -use crate::{ - ALPN, TunnelProtocol, - common::{Client2HostControlMsg, LocalClientConnection, TunnelCommon, receive_packet}, - get_unspecified, - net::{tcp::bi_tcp_stream_host, udp::bi_udp_host}, -}; - -#[derive(Error, Debug)] -pub enum HostError { - #[error("Tunnel with local addr {0} already exists")] - TunnelAlreadyExists(SocketAddr), - #[error("Failed to create endpoint: {0}")] - FailedToCreateEndpoint(String), - #[error("Failed to accept connection: {0}")] - FailedToAcceptConnection(String), - #[error("Failed to create stream: {0}")] - FailedToCreateStream(String), - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - #[error("Write error: {0}")] - WriteError(#[from] WriteError), - #[error("Read error: {0}")] - ReadError(#[from] ReadError), - #[error("Connection error: {0}")] - ConnectionError(#[from] iroh::endpoint::ConnectionError), - #[error("Tunnel is not running")] - TunnelNotRunning, - #[error("Receive packet error: {0}")] - ReceivePacketError(#[from] crate::common::ReceivePacketError), - #[error("Unexpected control message: {0:?}")] - UnexpectedControlMessage(Client2HostControlMsg), - #[error("Tunnel is already open")] - TunnelAlreadyOpen, -} - -/// The host state. -pub struct HostState { - tunnels: HashMap<[u8; 32], HostTunnel>, -} - -impl Default for HostState { - fn default() -> Self { - Self::new() - } -} - -impl HostState { - /// Create a new host state. - pub fn new() -> Self { - Self { - tunnels: HashMap::new(), - } - } - - /// Get the tunnels. - pub fn tunnels(&self) -> &HashMap<[u8; 32], HostTunnel> { - &self.tunnels - } - - /// Get the tunnels mutable. - pub fn tunnels_mut(&mut self) -> &mut HashMap<[u8; 32], HostTunnel> { - &mut self.tunnels - } - - /// Create a new tunnel. - pub async fn create_tunnel( - &mut self, - local_addr: SocketAddr, - name: String, - secret: [u8; 32], - protocol: TunnelProtocol, - ) -> &mut HostTunnel { - let tunnel = HostTunnel { - name: name.clone(), - secret: secret.into(), - host_addr: local_addr, - protocol, - is_open: Arc::new(AtomicBool::new(false)), - running_props: None, - }; - - self.tunnels.insert(secret, tunnel); - - self.tunnels.get_mut(&secret).unwrap() - } - - pub fn get_tunnel(&self, secret: &[u8; 32]) -> Option<&HostTunnel> { - self.tunnels.get(secret) - } - - pub fn get_tunnel_mut(&mut self, secret: &[u8; 32]) -> Option<&mut HostTunnel> { - self.tunnels.get_mut(secret) - } -} - -/// A [`HostTunnel`] will relay traffic to and from the `host_addr` to the connected clients -/// (all clients that know this tunnel's `secret`). -pub struct HostTunnel { - /// The name of the tunnel. - pub name: String, - /// The secret allows others to connect to this tunnel. - /// This value is unique for every existing HostTunnel. - secret: SecretKey, - /// The local socket address this tunnel is bound to. - host_addr: SocketAddr, - /// The protocol of the tunnel. - protocol: TunnelProtocol, - /// If the tunnel is open. - is_open: Arc, - /// Running tunnel values. - // TODO: type state pattern. - running_props: Option, -} - -/// Clients connected to the tunnel. -pub struct ConnectedClients { - /// A tunnel can have multiple connected clients. - tunnel_clients: HashMap, -} - -/// A single connected client. -#[derive(Clone)] -pub struct ConnectedClient { - /// A client can have multiple local connections. - /// e.g 2 clients running on the same machine connecting to the same tunnel. - /// - /// The key is the virtual address of the client (which is used on the host). - local_connections: Arc>>>>, - /// The connection to the client. - #[allow(dead_code)] - connection: Connection, -} - -/// The properties of the running host tunnel. -struct RunningHostTunnelProps { - /// The active connections of the tunnel. - connections: Arc>, - /// Quinn endpoint. - endpoint: Endpoint, - /// Channel sender to stop the entire tunnel. - stop_tx: watch::Sender, -} - -impl HostTunnel { - /// Start the tunnel loop. - pub async fn start(&mut self) -> Result>, HostError> { - tracing::debug!("Starting host tunnel \"{}\"", self.name); - if self.is_running() { - tracing::warn!( - "Host tunnel {} is already open, not starting again", - self.name - ); - return Err(HostError::TunnelAlreadyOpen); - } - - let endpoint = Endpoint::builder() - .alpns(vec![ALPN.to_vec()]) - .secret_key(self.secret.clone()) - .relay_mode(RelayMode::Default) - .discovery_n0() - .bind() - .await - .map_err(|e| HostError::FailedToCreateEndpoint(e.to_string()))?; - - let connections = Arc::new(RwLock::new(ConnectedClients { - tunnel_clients: HashMap::new(), - })); - - let tunnel_name = self.name.clone(); - let (stop_tx, stop_rx) = watch::channel(false); - - self.running_props = Some(RunningHostTunnelProps { - connections: connections.clone(), - endpoint: endpoint.clone(), - stop_tx, - }); - self.is_open.store(true, Ordering::SeqCst); - - let local_addr = self.host_addr; - let protocol = self.protocol; - - // Thread to handle incoming connections. - let conn_handler = tokio::spawn(async move { - loop { - let conn = endpoint.accept().await; - let Some(conn) = conn else { - tracing::debug!("Failed to accept connection"); - continue; - }; - - let conn = match conn.await { - Ok(conn) => conn, - Err(e) => { - tracing::debug!("Failed to accept connection: {}", e); - continue; - } - }; - - let peer_public_key = conn - .remote_node_id() - .expect("Failed to get peer public key"); - - { - let mut connections = connections.write().await; - if connections.tunnel_clients.contains_key(&peer_public_key) { - tracing::warn!( - "Host tunnel \"{}\" already has a connection from {}, ignoring", - tunnel_name, - peer_public_key - ); - continue; - } - - connections.tunnel_clients.insert( - peer_public_key, - ConnectedClient { - local_connections: Arc::new(RwLock::new(HashMap::new())), - connection: conn.clone(), - }, - ); - } // drop lock - - tracing::debug!( - "Host tunnel \"{}\" accepted a new connection from {}", - tunnel_name, - peer_public_key - ); - - match HostTunnel::handle_connection( - conn, - local_addr, - protocol, - connections.clone(), - stop_rx.clone(), - ) - .await - { - Ok(_) => { - tracing::debug!("Host tunnel \"{}\" connection closed", tunnel_name); - } - Err(e) => { - tracing::error!("Host tunnel \"{}\" connection error: {}", tunnel_name, e); - } - } - } - }); - - tracing::info!("Host tunnel \"{}\" started", self.name); - - Ok(conn_handler) - } - - async fn handle_connection( - conn: Connection, - tunnel_local_addr: SocketAddr, - protocol: TunnelProtocol, - connections: Arc>, - stop_rx: watch::Receiver, - ) -> Result<(), HostError> { - loop { - let (tunnel_send, tunnel_recv) = conn.accept_bi().await?; - // Get the local address of the local client connection. - let c2h_conn_req: Client2HostControlMsg = receive_packet(&conn).await?; - let local_addr = match c2h_conn_req { - Client2HostControlMsg::ConnReq { local_addr } => local_addr, - }; - - let client_virtual_addr = get_unspecified(local_addr.is_ipv4()); - - let local_client_connection = Arc::new(RwLock::new(LocalClientConnection { - client_local_addr: local_addr, - client_virtual_addr, - last_active: Arc::new(AtomicU64::new(0)), - })); - - let connected_client: Arc; - - { - let mut connections = connections.write().await; - let client = connections - .tunnel_clients - .get_mut(&conn.remote_node_id().unwrap()) - .unwrap(); - client - .local_connections - .write() - .await - .insert(client_virtual_addr, local_client_connection.clone()); - connected_client = Arc::new(client.clone()); - } // drop lock - - match protocol { - TunnelProtocol::Tcp => { - let socket = if tunnel_local_addr.is_ipv4() { - TcpSocket::new_v4()? - } else { - TcpSocket::new_v6()? - }; - - socket.bind(client_virtual_addr)?; - let stream = socket.connect(tunnel_local_addr).await?; - - bi_tcp_stream_host( - stream, - connected_client, - local_client_connection.clone(), - tunnel_send, - tunnel_recv, - stop_rx.clone(), - ) - .await?; - } - TunnelProtocol::Udp => { - let socket = UdpSocket::bind(client_virtual_addr).await?; - socket.connect(tunnel_local_addr).await?; - - bi_udp_host( - socket, - connected_client, - local_client_connection.clone(), - tunnel_send, - tunnel_recv, - stop_rx.clone(), - ) - .await?; - } - } - } - } - - /// Static method to disconnect a remote client connection, which closes all related - /// streams and sockets. - /// This is static so it can be called from other threads given the connections. - /// - /// # Returns - /// * `true` if the connection was successfully disconnected - /// * `false` otherwise. - pub async fn disconnect_local_client_connection( - connection: Arc, - local_connection: Arc>, - ) -> bool { - let virtual_addr = local_connection.read().await.client_virtual_addr; - let mut connections = connection.local_connections.write().await; - - if connections.remove(&virtual_addr).is_some() { - tracing::debug!( - "Disconnected local connection {}. Connections left: {}", - virtual_addr, - connections.len() - ); - true - } else { - tracing::warn!("Tried disconnecting a local connection that does not exist"); - false - } - } - - /// Stop the tunnel. - /// This will disconnect all clients and stop the tunnel. - pub async fn stop(&mut self) -> Result<(), HostError> { - if !self.is_running() { - tracing::warn!("Tunnel \"{}\" is already stopped", self.name); - return Ok(()); - } - - if let Some(running_props) = self.running_props.take() { - running_props.stop_tx.send(true).unwrap(); - running_props.endpoint.close().await; - } - - self.is_open.store(false, Ordering::SeqCst); - tracing::info!("Tunnel \"{}\" stopped", self.name); - Ok(()) - } - - /// Get the numbre of connected clients. - pub async fn num_clients(&self) -> usize { - if let Some(running_props) = &self.running_props { - let connections = running_props.connections.read().await; - connections.tunnel_clients.len() - } else { - 0 - } - } -} - -impl TunnelCommon for HostTunnel { - fn secret(&self) -> [u8; 32] { - self.secret.to_bytes() - } - - fn protocol(&self) -> TunnelProtocol { - self.protocol - } - - fn is_running(&self) -> bool { - self.is_open.load(Ordering::SeqCst) - } - - fn name(&self) -> String { - self.name.clone() - } - - async fn num_active_connections(&self) -> usize { - if let Some(running_props) = &self.running_props { - let mut local_connections = 0; - for client in running_props - .connections - .read() - .await - .tunnel_clients - .values() - { - local_connections += client.local_connections.read().await.len(); - } - - local_connections - } else { - 0 - } - } - - fn addr(&self) -> SocketAddr { - self.host_addr - } -} diff --git a/lantun-core/src/lib.rs b/lantun-core/src/lib.rs deleted file mode 100644 index 0d4df76..0000000 --- a/lantun-core/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -mod client; -mod common; -mod host; -mod net; -mod utils; - -use client::ClientState; -use host::HostState; -use iroh::SecretKey; -use rand::rngs::OsRng; -use std::{ - net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, - str::FromStr, -}; - -pub use client::ClientTunnel; -pub use common::TunnelCommon; -pub use host::HostTunnel; - -pub const ALPN: &[u8] = b"lan-tun/0.1.0"; -pub const LANTUN_VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub fn get_unspecified(ip4: bool) -> SocketAddr { - if ip4 { - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)) - } else { - SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TunnelProtocol { - Tcp, - Udp, -} - -impl std::fmt::Display for TunnelProtocol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TunnelProtocol::Tcp => write!(f, "tcp"), - TunnelProtocol::Udp => write!(f, "udp"), - } - } -} - -impl FromStr for TunnelProtocol { - type Err = std::io::Error; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "tcp" => Ok(TunnelProtocol::Tcp), - "udp" => Ok(TunnelProtocol::Udp), - _ => Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("Invalid protocol: {}", s), - )), - } - } -} - -impl serde::Serialize for TunnelProtocol { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let s = match self { - TunnelProtocol::Tcp => "tcp", - TunnelProtocol::Udp => "udp", - }; - serializer.serialize_str(s) - } -} - -impl<'de> serde::Deserialize<'de> for TunnelProtocol { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - TunnelProtocol::from_str(&s).map_err(serde::de::Error::custom) - } -} - -/// The state of the LanTun application. -/// This tracks both the host and client state, -/// so a user can use other tunnels as well as create their own. -pub struct LanTun { - /// The state of the tunnels hosted by the user. - pub host: HostState, - /// The state of the tunnels this user is connected to. - pub client: ClientState, -} - -impl Default for LanTun { - fn default() -> Self { - Self::new() - } -} - -impl LanTun { - pub fn new() -> Self { - let host_state = HostState::new(); - let client_state = ClientState::new(); - - LanTun { - host: host_state, - client: client_state, - } - } -} - -pub fn gen_secret() -> SecretKey { - let mut rng = OsRng; - SecretKey::generate(&mut rng) -} diff --git a/lantun-core/src/net/mod.rs b/lantun-core/src/net/mod.rs deleted file mode 100644 index 47f45ae..0000000 --- a/lantun-core/src/net/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod tcp; -pub mod udp; -// pub mod forwarder; diff --git a/lantun-core/src/net/tcp.rs b/lantun-core/src/net/tcp.rs deleted file mode 100644 index deb9957..0000000 --- a/lantun-core/src/net/tcp.rs +++ /dev/null @@ -1,267 +0,0 @@ -use std::{ - collections::HashMap, - net::SocketAddr, - sync::{Arc, atomic::Ordering}, - time::Duration, -}; - -use iroh::endpoint::{RecvStream, SendStream}; -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream, - sync::{RwLock, watch}, -}; - -use crate::{ - client::{ClientError, ClientTunnel}, - common::{CONNECTION_TIMEOUT_SECONDS, LocalClientConnection, TUNNEL_FORWARDER_BUFFER_SIZE}, - host::{ConnectedClient, HostError, HostTunnel}, - utils::now_secs, -}; - -pub async fn bi_tcp_stream_host( - // The stream of the virtual client to the tunneled socket. - tcp_stream: TcpStream, - connection: Arc, - // The current connection of the client to the host. - local_client_connection: Arc>, - mut tunnel_send: SendStream, - mut tunnel_recv: RecvStream, - mut stop_rx: watch::Receiver, -) -> Result<(), HostError> { - tracing::debug!("Starting bidirectional TCP forwarder for the host tunnel"); - - let (mut tcp_read, mut tcp_write) = tcp_stream.into_split(); - let stop_rx_c = stop_rx.clone(); - let last_active = local_client_connection.read().await.last_active.clone(); - let last_active_c = last_active.clone(); - - last_active.store(now_secs(), Ordering::SeqCst); - - let tunnel_to_tcp_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - let mut stop_rx = stop_rx_c; - let last_active = last_active_c; - - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping TCP stream for host tunnel"); - break; - } - } - Ok(n) = tunnel_recv.read(&mut buf) => { - let Some(n) = n else { - tracing::debug!("Tunnel stream closed"); - break; - }; - - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tcp_write.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("TCP stream timed out"); - break; - } - } - } - } - }); - - let tcp_to_tunnel_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping TCP stream for host tunnel"); - break; - } - } - Ok(n) = tcp_read.read(&mut buf) => { - if n == 0 { - tracing::debug!("TCP stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tunnel_send.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("TCP stream timed out"); - break; - } - } - } - } - }); - - tracing::info!("Started Tunnel TCP forwarder for host tunnel"); - - let _ = tokio::try_join!(tunnel_to_tcp_forwader, tcp_to_tunnel_forwader); - - HostTunnel::disconnect_local_client_connection(connection, local_client_connection).await; - - tracing::info!("Tunnel TCP forwarder for host tunnel stopped"); - - Ok(()) -} - -pub async fn bi_tcp_stream_client( - // The stream of the client application to the tunnel socket. - tcp_stream: TcpStream, - // All connections on this tunnel. - local_connections: Arc>>>>, - // The current connection of the client to the host (on this tunnel). - local_client_connection: Arc>, - mut tunnel_send: SendStream, - mut tunnel_recv: RecvStream, - mut stop_rx: watch::Receiver, -) -> Result<(), ClientError> { - tracing::debug!("Starting bidirectional TCP forwarder for the client tunnel"); - - let (mut tcp_read, mut tcp_write) = tcp_stream.into_split(); - let stop_rx_c = stop_rx.clone(); - let last_active = local_client_connection.read().await.last_active.clone(); - let last_active_c = last_active.clone(); - - last_active.store(now_secs(), Ordering::SeqCst); - - let tunnel_to_tcp_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - let mut stop_rx = stop_rx_c; - let last_active = last_active_c; - - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping TCP stream for client tunnel"); - break; - } - } - Ok(n) = tunnel_recv.read(&mut buf) => { - let Some(n) = n else { - tracing::debug!("Tunnel stream closed"); - break; - }; - - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tcp_write.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("TCP stream timed out"); - break; - } - } - } - } - }); - - let tcp_to_tunnel_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping TCP stream for client tunnel"); - break; - } - } - Ok(n) = tcp_read.read(&mut buf) => { - if n == 0 { - tracing::debug!("TCP stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tunnel_send.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("TCP stream timed out"); - break; - } - } - } - } - }); - - tracing::info!("Started Tunnel TCP forwarder for client tunnel"); - - let _ = tokio::try_join!(tunnel_to_tcp_forwader, tcp_to_tunnel_forwader); - - ClientTunnel::disconnect_local_connection(local_connections, local_client_connection).await; - - tracing::info!("Tunnel TCP forwarder for client tunnel stopped"); - - Ok(()) -} diff --git a/lantun-core/src/net/udp.rs b/lantun-core/src/net/udp.rs deleted file mode 100644 index 89f3eac..0000000 --- a/lantun-core/src/net/udp.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::{ - collections::HashMap, - net::SocketAddr, - sync::{Arc, atomic::Ordering}, - time::Duration, -}; - -use iroh::endpoint::{RecvStream, SendStream}; -use tokio::{ - net::UdpSocket, - sync::{RwLock, watch}, -}; - -use crate::{ - client::{ClientError, ClientTunnel}, - common::{CONNECTION_TIMEOUT_SECONDS, LocalClientConnection, TUNNEL_FORWARDER_BUFFER_SIZE}, - host::{ConnectedClient, HostError, HostTunnel}, - utils::now_secs, -}; - -pub async fn bi_udp_host( - // The virtual client socket - socket: UdpSocket, - connection: Arc, - // The current connection of the client to the host. - local_client_connection: Arc>, - mut tunnel_send: SendStream, - mut tunnel_recv: RecvStream, - mut stop_rx: watch::Receiver, -) -> Result<(), HostError> { - if socket.peer_addr().is_err() { - panic!("Socket is not connected"); - } - - tracing::debug!("Starting bidirectional UDP forwarder for the host tunnel"); - - let socket_arc = Arc::new(socket); - - let udp_read = socket_arc.clone(); - let udp_write = socket_arc.clone(); - - let stop_rx_c = stop_rx.clone(); - let last_active = local_client_connection.read().await.last_active.clone(); - let last_active_c = last_active.clone(); - - last_active.store(now_secs(), Ordering::SeqCst); - - let tunnel_to_udp_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - let mut stop_rx = stop_rx_c; - let last_active = last_active_c; - - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping UDP stream for host tunnel"); - break; - } - } - Ok(n) = tunnel_recv.read(&mut buf) => { - let Some(n) = n else { - tracing::debug!("Tunnel stream closed"); - break; - }; - - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - udp_write.send(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("UDP stream timed out"); - break; - } - } - } - } - }); - - let udp_to_tunnel_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping UDP stream for host tunnel"); - break; - } - } - Ok(n) = udp_read.recv(&mut buf) => { - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tunnel_send.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("UDP stream timed out"); - break; - } - } - } - } - }); - - tracing::info!("Started tunnel UDP forwarder for host tunnel"); - - let _ = tokio::try_join!(tunnel_to_udp_forwader, udp_to_tunnel_forwader); - - HostTunnel::disconnect_local_client_connection(connection, local_client_connection).await; - - tracing::info!("Stopping tunnel UDP forwarder for host tunnel"); - - Ok(()) -} - -pub async fn bi_udp_client( - // The socket of the client application to the tunnel socket. - socket: Arc, - // All connections on this tunnel. - local_connections: Arc>>>>, - // The current connection of the client to the host (on this tunnel). - local_client_connection: Arc>, - mut tunnel_send: SendStream, - mut tunnel_recv: RecvStream, - mut stop_rx: watch::Receiver, -) -> Result<(), ClientError> { - tracing::debug!("Starting bidirectional UDP forwarder for the client tunnel"); - - // The address of the client application connecting to the tunnel. - let client_local_addr = { local_client_connection.read().await.client_local_addr }; - - let udp_read = socket.clone(); - let udp_write = socket; - - let stop_rx_c = stop_rx.clone(); - let last_active = local_client_connection.read().await.last_active.clone(); - let last_active_c = last_active.clone(); - - last_active.store(now_secs(), Ordering::SeqCst); - - let tunnel_to_udp_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - let mut stop_rx = stop_rx_c; - let last_active = last_active_c; - - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping UDP stream for host tunnel"); - break; - } - } - Ok(n) = tunnel_recv.read(&mut buf) => { - let Some(n) = n else { - tracing::debug!("Tunnel stream closed"); - break; - }; - - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - udp_write.send_to(&buf[..n], client_local_addr).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("UDP stream timed out"); - break; - } - } - } - } - }); - - let udp_to_tunnel_forwader = tokio::spawn(async move { - let mut buf = vec![0; TUNNEL_FORWARDER_BUFFER_SIZE]; - loop { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - let timeout = if elapsed >= CONNECTION_TIMEOUT_SECONDS { - Duration::from_secs(0) - } else { - Duration::from_secs(CONNECTION_TIMEOUT_SECONDS - elapsed) - }; - - tokio::select! { - biased; - _ = stop_rx.changed() => { - if *stop_rx.borrow() { - tracing::debug!("Stopping UDP stream for host tunnel"); - break; - } - } - Ok((n, addr)) = udp_read.peek_from(&mut buf) => { - if addr != client_local_addr { - continue - }; - - if udp_read.recv(&mut buf[..n]).await.is_err() { - // We can safely ignore this case, - // the thread corresponding to the unknown IP will read the data. - continue; - }; - - if n == 0 { - tracing::debug!("Tunnel stream closed"); - break; - } - - last_active.store(now_secs(), Ordering::SeqCst); - tunnel_send.write_all(&buf[..n]).await.unwrap(); - } - _ = tokio::time::sleep(timeout) => { - let now = now_secs(); - let last = last_active.load(Ordering::SeqCst); - let elapsed = now.saturating_sub(last); - - if elapsed >= CONNECTION_TIMEOUT_SECONDS { - tracing::debug!("UDP stream timed out"); - break; - } - } - } - } - }); - - tracing::info!("Started tunnel UDP forwarder for host tunnel"); - - let _ = tokio::try_join!(tunnel_to_udp_forwader, udp_to_tunnel_forwader); - - ClientTunnel::disconnect_local_connection(local_connections, local_client_connection).await; - - Ok(()) -} diff --git a/lantun-core/src/utils.rs b/lantun-core/src/utils.rs deleted file mode 100644 index 8e1d258..0000000 --- a/lantun-core/src/utils.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::time::{SystemTime, UNIX_EPOCH}; - -pub fn now_secs() -> u64 { - let now = SystemTime::now(); - let duration = now.duration_since(UNIX_EPOCH).unwrap(); - duration.as_secs() -} diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..7aaf6f2 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,369 @@ +use crate::common::{FilteredUdpReader, FilteredUdpWriter}; +use crate::{ + common::{ALPN, Protocol, TcpStreamOrUdpSocket}, + config::ClientTunnelConfig, + forwarder::forwarder, +}; +use iroh::{Endpoint, PublicKey, endpoint::Connection}; +use std::{collections::HashMap, net::SocketAddr, sync::Arc}; +use thiserror::Error; +use tokio::{ + net::{TcpListener, UdpSocket}, + sync::RwLock, +}; + +#[derive(Error, Debug)] +pub enum ClientError { + #[error("Failed to bind iroh endpoint: {0}")] + EndpointBindError(#[from] iroh::endpoint::BindError), + #[error("Failed to connect to host tunnel: {0}")] + FailedToConnect(#[from] iroh::endpoint::ConnectError), + #[error("IO error: {0}")] + IoError(#[from] std::io::Error), + #[error("Connection error: {0}")] + ConnectionError(#[from] iroh::endpoint::ConnectionError), + #[error("Read exact error: {0}")] + ReadExactError(#[from] iroh::endpoint::ReadExactError), + #[error("Write error: {0}")] + WriteError(#[from] iroh::endpoint::WriteError), +} + +/// Marker type for a stopped client tunnel +pub struct Stopped; +/// Marker type for a running client tunnel +pub struct Running(Arc>); + +#[derive(Debug)] +pub struct ActiveProps { + /// Maps local client addresses to their underlying connections. + local_connections: Arc>>, + /// The endpoint of the connection. + _endpoint: Endpoint, + /// The handle to the local connection acceptor task + /// (We want a separate data channel for each local connection, + /// so we can have multiple local connections coming through the same tunnel) + conn_acceptor: tokio::task::JoinHandle<()>, +} + +#[derive(Debug)] +struct LocalConnection { + /// The local socket address that exposes the tunnel + local_addr: SocketAddr, + /// The handle to the forwarder task + forwarder: tokio::task::JoinHandle<()>, +} + +impl LocalConnection { + /// Creates a new local connection from an accepted TCP stream + async fn new( + connection: &Connection, + stream: TcpStreamOrUdpSocket, + local_addr: SocketAddr, + all_connections: Arc>>, + ) -> Result { + tracing::info!( + "Accepted local TCP connection for client tunnel at {}", + local_addr + ); + + // let (stream_read, stream_write) = stream.into_split(); + tracing::debug!("Opening bi-directional stream to host"); + + // Client opens a bi-directional stream to the host + let (mut channel_write, channel_read) = connection.open_bi().await?; + + // Send opening byte to host + channel_write.write_all(&[0u8]).await?; + + let conn_closer = move || { + let all_connections = all_connections.clone(); + let local_addr = local_addr; + async move { + tracing::info!( + "Local connection at {} closed, removing from active connections", + local_addr + ); + let mut connections = all_connections.write().await; + connections.remove(&local_addr); + } + }; + + match stream { + TcpStreamOrUdpSocket::Tcp(stream) => { + let (stream_read, stream_write) = stream.into_split(); + + let forwarder = forwarder( + stream_read, + channel_write, + channel_read, + stream_write, + Default::default(), + conn_closer, + ); + + Ok(LocalConnection { + local_addr, + forwarder, + }) + } + TcpStreamOrUdpSocket::Udp(socket) => { + let stream_read = FilteredUdpReader { + socket: socket.clone(), + client_addr: local_addr, + }; + + let stream_write = FilteredUdpWriter { + socket: socket.clone(), + client_addr: local_addr, + }; + + let forwarder = forwarder( + stream_read, + channel_write, + channel_read, + stream_write, + Default::default(), + conn_closer, + ); + + Ok(LocalConnection { + local_addr, + forwarder, + }) + } + } + + // Ok(LocalConnection { + // local_addr, + // forwarder, + // }) + } +} + +pub struct ClientTunnel { + pub name: Option, + /// The public key of the remote host tunnel. + public: PublicKey, + /// The local address the remote tunnel is exposed on. + addr: SocketAddr, + proto: Protocol, + state: State, +} + +impl ClientTunnel { + /// Get the name of the tunnel + pub fn name(&self) -> Option<&String> { + self.name.as_ref() + } + + /// Get the public key of the remote host tunnel + pub fn host_public_key(&self) -> &PublicKey { + &self.public + } + + /// Get the local address the remote tunnel is exposed on + pub fn address(&self) -> &SocketAddr { + &self.addr + } + + /// Get the protocol of the tunnel + pub fn protocol(&self) -> &Protocol { + &self.proto + } +} + +impl ClientTunnel { + pub fn new( + name: Option, + public: PublicKey, + addr: SocketAddr, + protocol: Protocol, + ) -> Self { + ClientTunnel { + name, + public, + addr, + proto: protocol, + state: Stopped, + } + } + + /// Creates a client tunnel from config + pub fn from_config(config: &ClientTunnelConfig) -> Self { + Self::new( + config.name.clone(), + config.host_public, + config.local_addr, + config.proto, + ) + } + + pub async fn start(self) -> Result, ClientError> { + tracing::info!( + "Starting client tunnel \"{:?}\" to {}", + self.name, + self.addr + ); + + let endpoint = Endpoint::builder() + .alpns(vec![ALPN.to_vec()]) + .relay_mode(iroh::RelayMode::Default) + .bind() + .await + .map_err(ClientError::EndpointBindError)?; + + let conn = endpoint + .connect(self.public, ALPN) + .await + .map_err(ClientError::FailedToConnect)?; + + tracing::info!( + "Client tunnel \"{:?}\" connected to host at {}", + self.name, + self.addr + ); + + let local_connections = Arc::new(RwLock::new(HashMap::new())); + + let conn_acceptor = { + let local_connections = local_connections.clone(); + let proto = self.proto; + let addr = self.addr; + + tokio::spawn(async move { + // For TCP, create a single listener that accepts multiple connections + match proto { + Protocol::Tcp => { + let Ok(listener) = TcpListener::bind(addr).await else { + tracing::error!( + "Failed to bind TCP listener for client tunnel at {}", + addr + ); + return; + }; + + loop { + let Ok((stream, client_addr)) = listener.accept().await else { + continue; + }; + + let stream = TcpStreamOrUdpSocket::from_tcp(stream); + + let Ok(local_conn) = LocalConnection::new( + &conn, + stream, + client_addr, + local_connections.clone(), + ) + .await + else { + tracing::error!("Failed to create local connection"); + continue; + }; + + { + let mut connections = local_connections.write().await; + connections.insert(local_conn.local_addr, local_conn); + } + } + } + Protocol::Udp => { + let Ok(socket) = UdpSocket::bind(addr).await else { + tracing::error!( + "Failed to bind UDP socket for client tunnel at {}", + addr + ); + return; + }; + + let socket = Arc::new(socket); + + loop { + let Ok(local_client_addr) = socket.peek_sender().await else { + continue; + }; + + // Check if we already have a local connection for this client + { + let connections = local_connections.read().await; + if connections.contains_key(&local_client_addr) { + continue; + } + } + + let socket = TcpStreamOrUdpSocket::from_udp(socket.clone()); + + let Ok(local_conn) = LocalConnection::new( + &conn, + socket, + local_client_addr, + local_connections.clone(), + ) + .await + else { + tracing::error!("Failed to create local UDP connection"); + continue; + }; + + { + let mut connections = local_connections.write().await; + connections.insert(local_conn.local_addr, local_conn); + } + } + } + } + }) + }; + + Ok(ClientTunnel { + name: self.name, + public: self.public, + addr: self.addr, + proto: self.proto, + state: Running(Arc::new(RwLock::new(ActiveProps { + local_connections, + _endpoint: endpoint, + conn_acceptor, + }))), + }) + } +} + +impl ClientTunnel { + /// Stops the client tunnel + pub async fn stop(self) -> ClientTunnel { + tracing::info!("Stopping client tunnel \"{:?}\"", self.name); + + let active_props = Arc::try_unwrap(self.state.0) + .expect("No other references to active props exist") + .into_inner(); + + // Stop all local connections + let local_conns = active_props.local_connections.read().await; + for (_addr, local_conn) in local_conns.iter() { + local_conn.forwarder.abort(); + } + + // Stop connection acceptor + active_props.conn_acceptor.abort(); + + ClientTunnel { + name: self.name, + public: self.public, + addr: self.addr, + proto: self.proto, + state: Stopped, + } + } + + pub async fn num_local_connections(&self) -> usize { + self.state + .0 + .read() + .await + .local_connections + .read() + .await + .len() + } +} diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000..ee9973a --- /dev/null +++ b/src/common.rs @@ -0,0 +1,240 @@ +use std::{ + net::SocketAddr, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; + +use tokio::{ + io::{AsyncRead, AsyncWrite}, + net::{TcpStream, UdpSocket}, +}; + +pub const ALPN: &[u8] = b"lan-tun/0.2.0"; + +#[derive(Clone, Copy, Debug)] +pub enum Protocol { + Tcp, + Udp, +} + +impl serde::Serialize for Protocol { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Protocol::Tcp => serializer.serialize_str("tcp"), + Protocol::Udp => serializer.serialize_str("udp"), + } + } +} + +impl<'de> serde::Deserialize<'de> for Protocol { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "tcp" => Ok(Protocol::Tcp), + "udp" => Ok(Protocol::Udp), + _ => Err(serde::de::Error::custom(format!("Unknown protocol: {}", s))), + } + } +} + +impl std::fmt::Display for Protocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Protocol::Tcp => write!(f, "tcp"), + Protocol::Udp => write!(f, "udp"), + } + } +} + +impl std::str::FromStr for Protocol { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "tcp" => Ok(Protocol::Tcp), + "udp" => Ok(Protocol::Udp), + _ => Err(format!("Unknown protocol: {}", s)), + } + } +} + +pub enum TcpStreamOrUdpSocket { + Tcp(TcpStream), + Udp(Arc), +} + +impl TcpStreamOrUdpSocket { + pub fn from_tcp(stream: TcpStream) -> Self { + TcpStreamOrUdpSocket::Tcp(stream) + } + + pub fn from_udp(socket: Arc) -> Self { + TcpStreamOrUdpSocket::Udp(socket) + } +} + +pub struct FilteredUdpReader { + pub socket: Arc, + pub client_addr: SocketAddr, +} + +pub struct FilteredUdpWriter { + pub socket: Arc, + pub client_addr: SocketAddr, +} + +impl AsyncRead for FilteredUdpReader { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + let socket = self.socket.clone(); + + // Try to peek at the next packet's sender without consuming it + match socket.try_peek_sender() { + Ok(addr) => { + if addr == self.client_addr { + // This is from our client, consume and read it + let mut temp_buf = vec![0u8; buf.remaining()]; + match socket.try_recv_from(&mut temp_buf) { + Ok((n, _)) => { + buf.put_slice(&temp_buf[..n]); + Poll::Ready(Ok(())) + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + // Socket became empty between peek and recv + // Register waker and wait + let mut read_buf = tokio::io::ReadBuf::new(&mut temp_buf); + socket.poll_recv_from(cx, &mut read_buf).map(|_| Ok(())) + } + Err(e) => Poll::Ready(Err(e)), + } + } else { + // This packet is from a different client + // Wake ourselves up immediately to check again soon, + // but return Pending to let other forwarders run + cx.waker().wake_by_ref(); + Poll::Pending + } + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + // No packets available, register waker and wait + let mut temp_buf = vec![0u8; buf.remaining()]; + let mut read_buf = tokio::io::ReadBuf::new(&mut temp_buf); + socket.poll_recv_from(cx, &mut read_buf).map(|_| Ok(())) + } + Err(e) => Poll::Ready(Err(e)), + } + } +} + +impl AsyncWrite for FilteredUdpWriter { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let socket = self.socket.clone(); + match socket.try_send_to(buf, self.client_addr) { + Ok(n) => Poll::Ready(Ok(n)), + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + // Register waker and wait for socket to be writable + socket.poll_send_to(cx, buf, self.client_addr) + } + Err(e) => Poll::Ready(Err(e)), + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +pub struct UdpWriter { + pub socket: Arc, +} + +pub struct UdpReader { + pub socket: Arc, +} + +impl AsyncWrite for UdpWriter { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let socket = self.socket.clone(); + match socket.try_send(buf) { + Ok(n) => Poll::Ready(Ok(n)), + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + // Register waker and wait for socket to be writable + socket.poll_send(cx, buf) + } + Err(e) => Poll::Ready(Err(e)), + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl AsyncRead for UdpReader { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + let socket = self.socket.clone(); + let mut temp_buf = vec![0u8; buf.remaining()]; + match socket.try_recv_from(&mut temp_buf) { + Ok((n, _)) => { + buf.put_slice(&temp_buf[..n]); + Poll::Ready(Ok(())) + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + // Register waker and wait for data + let mut read_buf = tokio::io::ReadBuf::new(&mut temp_buf); + socket.poll_recv_from(cx, &mut read_buf).map(|_| Ok(())) + } + Err(e) => Poll::Ready(Err(e)), + } + } +} + +impl AsyncWrite for UdpReader { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + Poll::Ready(Err(std::io::Error::other( + "UdpReader does not support write", + ))) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..00e9fee --- /dev/null +++ b/src/config.rs @@ -0,0 +1,51 @@ +use std::{net::SocketAddr, path::Path}; + +use iroh::{PublicKey, SecretKey}; +use serde::{Deserialize, Serialize}; + +use crate::common::Protocol; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct LantunConfig { + pub host_tunnels: Vec, + pub client_tunnels: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HostTunnelConfig { + pub name: Option, + pub secret: SecretKey, + /// The Address that will be opened to the internet + pub local_addr: SocketAddr, + pub proto: Protocol, + pub enabled: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClientTunnelConfig { + pub name: Option, + /// The hosts public key + pub host_public: PublicKey, + /// The address to bind the remote service to + pub local_addr: SocketAddr, + pub proto: Protocol, + pub enabled: bool, +} + +pub fn load_config(path: &Path) -> color_eyre::Result { + if !path.exists() { + tracing::debug!("Config file not found, creating a new one at {:?}", path); + std::fs::create_dir_all(path.parent().unwrap())?; + std::fs::write(path, toml::ser::to_string(&LantunConfig::default())?)?; + return Ok(LantunConfig::default()); + } + + let config: LantunConfig = toml::de::from_str(&std::fs::read_to_string(path)?)?; + Ok(config) +} + +pub fn save_config(path: &Path, config: &LantunConfig) -> color_eyre::Result<()> { + std::fs::write(path, toml::ser::to_string(config)?)?; + tracing::debug!("Config file saved to {:?}", path); + Ok(()) +} diff --git a/src/forwarder.rs b/src/forwarder.rs new file mode 100644 index 0000000..0631b9f --- /dev/null +++ b/src/forwarder.rs @@ -0,0 +1,93 @@ +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +pub struct ForwarderConfig { + buffer_size: usize, + timeout: Option, +} + +impl Default for ForwarderConfig { + fn default() -> Self { + Self { + buffer_size: 65536, + timeout: None, + } + } +} + +/// Starts the forwarding between a local socket and a quic message stream +pub fn forwarder( + mut from1: RS, + mut to1: WC, + mut from2: RC, + mut to2: WS, + config: ForwarderConfig, + mut on_connection_closed: F, +) -> tokio::task::JoinHandle<()> +where + RS: AsyncReadExt + Unpin + Send + 'static, + WS: AsyncWriteExt + Unpin + Send + 'static, + RC: AsyncReadExt + Unpin + Send + 'static, + WC: AsyncWriteExt + Unpin + Send + 'static, + Fut: std::future::Future + Send, + F: FnMut() -> Fut + Send + 'static, +{ + tokio::spawn(async move { + let mut socket_buf = vec![0u8; config.buffer_size]; + let mut stream_buf = vec![0u8; config.buffer_size]; + + loop { + let timeout_future = match config.timeout { + Some(duration) => tokio::time::sleep(duration), + None => tokio::time::sleep(std::time::Duration::MAX), + }; + + tokio::select! { + // Read from socket and write to stream + res = from1.read(&mut socket_buf) => { + match res { + Ok(0) => { + tracing::debug!("Socket closed"); + break; + } + Ok(n) => { + if let Err(e) = to1.write_all(&socket_buf[..n]).await { + tracing::error!("Error writing to stream: {}", e); + break; + } + } + Err(e) => { + tracing::error!("Error reading from socket: {}", e); + break; + } + } + } + // Read from stream and write to socket + res = from2.read(&mut stream_buf) => { + match res { + Ok(0) => { + tracing::debug!("Stream closed"); + break; + } + Ok(n) => { + if let Err(e) = to2.write_all(&stream_buf[..n]).await { + tracing::error!("Error writing to socket: {}", e); + break; + } + } + Err(e) => { + tracing::error!("Error reading from stream: {}", e); + break; + } + } + } + // Timeout if configured + _ = timeout_future, if config.timeout.is_some() => { + tracing::warn!("Forwarding timeout reached"); + break; + } + } + } + + on_connection_closed().await; + }) +} diff --git a/src/host.rs b/src/host.rs new file mode 100644 index 0000000..11e4db2 --- /dev/null +++ b/src/host.rs @@ -0,0 +1,428 @@ +use crate::{ + common::{ALPN, Protocol, UdpReader, UdpWriter}, + config::HostTunnelConfig, + forwarder::forwarder, +}; +use iroh::{Endpoint, PublicKey, SecretKey, endpoint::Connection}; +use std::{ + collections::HashMap, + net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, + sync::Arc, +}; +use thiserror::Error; +use tokio::{ + net::{TcpSocket, UdpSocket}, + sync::RwLock, +}; + +#[derive(Error, Debug)] +pub enum HostError { + #[error("Failed to bind iroh endpoint: {0}")] + EndpointBind(#[from] iroh::endpoint::BindError), + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[error("Connection error: {0}")] + Connection(#[from] iroh::endpoint::ConnectionError), + #[error("Write error: {0}")] + Write(#[from] iroh::endpoint::WriteError), +} + +/// Marker type for a stopped tunnel +pub struct Stopped; + +/// Marker type for a running tunnel +pub struct Running(Arc>); + +/// Data available on an active host tunnel +pub struct ActiveProps { + /// Maps the local/virtual address of the client to the underlying connection. + active_connections: Arc>>, + /// Endpoint created by this tunnel. + _endpoint: Endpoint, + /// The handle to the connection acceptor task + conn_acceptor: tokio::task::JoinHandle<()>, +} + +struct ClientConnection { + /// The iroh connection. + connection: Connection, + /// The virtual address assigned to this client. + virtual_addr: SocketAddr, + /// The handle to the stream acceptor task that handles multiple bi-directional streams + stream_acceptor: tokio::task::JoinHandle<()>, +} + +impl ClientConnection { + pub async fn new( + connection: Connection, + local: SocketAddr, + proto: Protocol, + all_connections: Arc>>, + ) -> Result { + match proto { + Protocol::Tcp => { + // Spawn a task that accepts multiple bi-directional streams + // Each stream represents a separate TCP connection + let stream_acceptor = { + let connection = connection.clone(); + tokio::spawn(async move { + loop { + // Accept incoming bi-directional stream from client + let Ok((channel_write, mut channel_read)) = + connection.accept_bi().await + else { + tracing::debug!("Connection closed or failed to accept bi stream"); + break; + }; + + tracing::debug!("Accepted bi-directional stream for TCP connection"); + + let all_connections_clone = all_connections.clone(); + tokio::spawn(async move { + // Consume opening byte from client + if let Err(e) = channel_read.read_exact(&mut [0u8]).await { + tracing::error!("Failed to read opening byte: {}", e); + return; + } + + let client_addr = if local.is_ipv4() { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + 0, + 0, + 0, + )) + }; + + let socket = if local.is_ipv4() { + TcpSocket::new_v4().unwrap() + } else { + TcpSocket::new_v6().unwrap() + }; + + socket.bind(client_addr).unwrap(); + tracing::debug!("Connecting to local service at {}", local); + + let Ok(stream) = socket.connect(local).await else { + tracing::error!( + "Failed to connect to local service at {}", + local + ); + return; + }; + + let (stream_read, stream_write) = stream.into_split(); + + // Start forwarding data between the stream and the channel + let _forwarder = forwarder( + channel_read, + stream_write, + stream_read, + channel_write, + Default::default(), + move || { + let all_connections = all_connections_clone.clone(); + let local_addr = local; + async move { + tracing::info!( + "Local connection at {} closed, removing from active connections", + local_addr + ); + let mut connections = all_connections.write().await; + connections.remove(&local_addr); + } + }, + ); + + // Wait for forwarder to complete + _forwarder.await.ok(); + }); + } + }) + }; + + Ok(Self { + connection, + virtual_addr: local, + stream_acceptor, + }) + } + Protocol::Udp => { + let stream_acceptor = { + let connection = connection.clone(); + tokio::spawn(async move { + loop { + let Ok((channel_write, mut channel_read)) = + connection.accept_bi().await + else { + tracing::debug!("Connection closed or failed to accept bi stream"); + break; + }; + + tracing::debug!("Accepted bi-directional stream for UDP connection"); + + let all_connections_clone = all_connections.clone(); + tokio::spawn(async move { + // Consume opening byte from client + if let Err(e) = channel_read.read_exact(&mut [0u8]).await { + tracing::error!("Failed to read opening byte: {}", e); + return; + } + + let client_addr = if local.is_ipv4() { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + 0, + 0, + 0, + )) + }; + + let Ok(socket) = UdpSocket::bind(client_addr).await else { + tracing::error!( + "Failed to bind UDP socket for local service at {}", + local + ); + return; + }; + + tracing::debug!("Connecting to local UDP service at {}", local); + + if let Err(e) = socket.connect(local).await { + tracing::error!( + "Failed to connect UDP socket to local service at {}: {}", + local, + e + ); + return; + } + + let sock_arc = Arc::new(socket); + + let stream_read = UdpReader { + socket: sock_arc.clone(), + }; + + let stream_write = UdpWriter { + socket: sock_arc.clone(), + }; + + let _forwarder = forwarder( + channel_read, + stream_write, + stream_read, + channel_write, + Default::default(), + move || { + let all_connections = all_connections_clone.clone(); + let local_addr = local; + async move { + tracing::info!( + "Local UDP connection at {} closed, removing from active connections", + local_addr + ); + let mut connections = all_connections.write().await; + connections.remove(&local_addr); + } + }, + ); + // Wait for forwarder to complete + _forwarder.await.ok(); + }); + } + }) + }; + Ok(Self { + connection, + virtual_addr: local, + stream_acceptor, + }) + } + } + } +} + +/// A tunnel that exposes connections from our LAN to other peers. +pub struct HostTunnel { + pub name: Option, + secret: SecretKey, + /// The local address this tunnel exposes. + addr: SocketAddr, + proto: Protocol, + state: State, +} + +impl HostTunnel { + /// Get the name of the tunnel + pub fn name(&self) -> Option<&String> { + self.name.as_ref() + } + + /// Get the secret key of the tunnel + pub fn secret(&self) -> &SecretKey { + &self.secret + } + + /// Get the public key of the tunnel + pub fn public_key(&self) -> PublicKey { + self.secret.public() + } + + /// Get the address of the tunnel + pub fn address(&self) -> SocketAddr { + self.addr + } + + /// Get the protocol of the tunnel + pub fn protocol(&self) -> Protocol { + self.proto + } +} + +impl HostTunnel { + /// Create a new stopped tunnel + pub fn new(name: Option, secret: SecretKey, addr: SocketAddr, proto: Protocol) -> Self { + Self { + name, + secret, + addr, + proto, + state: Stopped, + } + } + + /// Create a host tunnel from config + pub fn from_config(config: &HostTunnelConfig) -> Self { + Self::new( + config.name.clone(), + config.secret.clone(), + config.local_addr, + config.proto, + ) + } + + /// Start the tunnel, transitioning to the Running state + pub async fn start(self) -> Result, HostError> { + tracing::info!("Starting host tunnel \"{:?}\" at {}", self.name, self.addr); + + let endpoint = Endpoint::builder() + .alpns(vec![ALPN.to_vec()]) + .secret_key(self.secret.clone()) + .relay_mode(iroh::RelayMode::Default) + .bind() + .await + .map_err(HostError::EndpointBind)?; + + tracing::info!("Host tunnel \"{:?}\" started", self.name); + + let active_connections = Arc::new(RwLock::new(HashMap::new())); + + let conn_acceptor = { + let endpoint = endpoint.clone(); + let active_connections = active_connections.clone(); + let proto = self.proto; + let addr = self.addr; + + tokio::spawn(async move { + loop { + let Some(incoming) = endpoint.accept().await else { + continue; + }; + + match incoming.accept() { + Ok(accepting) => { + let Ok(connection) = accepting.await else { + tracing::error!("Failed to establish connection"); + continue; + }; + + let conn = match ClientConnection::new( + connection, + addr, + proto, + active_connections.clone(), + ) + .await + { + Ok(c) => c, + Err(e) => { + tracing::error!("Failed to create client connection: {}", e); + continue; + } + }; + + tracing::info!( + "Client connected with virtual address {}", + conn.virtual_addr + ); + + active_connections + .write() + .await + .insert(conn.virtual_addr, conn); + } + Err(e) => { + tracing::error!("Failed to accept connection: {}", e); + } + } + } + }) + }; + + let active_props = Arc::new(RwLock::new(ActiveProps { + active_connections: active_connections.clone(), + _endpoint: endpoint, + conn_acceptor, + })); + + Ok(HostTunnel { + name: self.name, + secret: self.secret, + addr: self.addr, + proto: self.proto, + state: Running(active_props), + }) + } +} + +impl HostTunnel { + /// Stop the tunnel + pub async fn stop(self) -> HostTunnel { + tracing::info!("Stopping host tunnel \"{:?}\"", self.name); + + // Close all active connections + let active_props = self.state.0.read().await; + let active_connections = active_props.active_connections.read().await; + for (_addr, conn) in active_connections.iter() { + conn.connection.close(0u32.into(), b""); + conn.stream_acceptor.abort(); + } + + active_props.conn_acceptor.abort(); + + tracing::info!("Host tunnel \"{:?}\" stopped", self.name); + + HostTunnel { + name: self.name, + secret: self.secret, + addr: self.addr, + proto: self.proto, + state: Stopped, + } + } + + pub async fn num_connections(&self) -> usize { + self.state + .0 + .read() + .await + .active_connections + .read() + .await + .len() + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f7da5c1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,186 @@ +mod client; +mod common; +mod config; +mod forwarder; +mod host; + +#[cfg(test)] +mod tests; + +use clap::Parser; +use clap::Subcommand; +use iroh::PublicKey; +use std::net::SocketAddr; +use std::path::PathBuf; + +use crate::client::ClientTunnel; +use crate::common::Protocol; +use crate::config::HostTunnelConfig; +use crate::config::LantunConfig; +use crate::config::load_config; +use crate::config::save_config; +use crate::host::HostTunnel; + +fn default_config_file() -> PathBuf { + dirs::config_dir() + .expect("Failed to get config directory") + .join("lantun") + .join("lantun.toml") +} + +#[derive(Parser, Debug)] +#[clap(version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"))] +struct Args { + #[clap(long, short, default_value = "warn")] + /// Log Level + log_level: tracing::Level, + /// Config file + #[clap(long, short, default_value = default_config_file().to_string_lossy().to_string())] + config: PathBuf, + /// Action to perform + #[clap(subcommand)] + action: Option, +} + +#[derive(Debug, Subcommand)] +enum Action { + #[clap(name = "create-tunnel")] + CreateHostTunnel { + /// The local address this tunnel exposes. + local: SocketAddr, + /// The protocol to use `tcp` or `udp` + protocol: Protocol, + name: Option, + }, + #[clap(name = "add-client-tunnel")] + AddClientTunnel { + /// The local address the remote tunnel is exposed on. + local: SocketAddr, + /// The protocol to use `tcp` or `udp` + protocol: Protocol, + /// The public key of the remote host tunnel. + public_key: PublicKey, + name: Option, + }, +} + +pub struct State { + pub active_host_tunnels: Vec>, + pub inactive_host_tunnels: Vec, + + pub active_client_tunnels: Vec>, + pub inactive_client_tunnels: Vec, +} + +impl State { + pub async fn run_from_config(config: &LantunConfig) -> color_eyre::Result { + let mut active_host_tunnels = Vec::new(); + let mut inactive_host_tunnels = Vec::new(); + let mut active_client_tunnels = Vec::new(); + let mut inactive_client_tunnels = Vec::new(); + + for host_tunnel_config in &config.host_tunnels { + let host_tunnel = HostTunnel::from_config(host_tunnel_config); + + if host_tunnel_config.enabled { + active_host_tunnels.push(host_tunnel.start().await?); + } else { + inactive_host_tunnels.push(host_tunnel); + } + } + + for client_tunnel_config in &config.client_tunnels { + let client_tunnel = ClientTunnel::from_config(client_tunnel_config); + + if client_tunnel_config.enabled { + active_client_tunnels.push(client_tunnel.start().await?); + } else { + inactive_client_tunnels.push(client_tunnel); + } + } + + Ok(Self { + active_host_tunnels, + inactive_host_tunnels, + active_client_tunnels, + inactive_client_tunnels, + }) + } +} + +#[tokio::main] +async fn main() -> color_eyre::Result<()> { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .without_time() + .init(); + + let args = Args::parse(); + + tracing::debug!("Lantun {}", env!("CARGO_PKG_VERSION")); + + let mut config = load_config(&args.config)?; + + match args.action { + None => { + tracing::info!("Running lantun with config: {:?}", args.config); + + let _state = State::run_from_config(&config).await?; + } + Some(action) => match action { + Action::CreateHostTunnel { + local, + protocol, + name, + } => { + let mut rng = rand::rng(); + tracing::info!( + "Creating host tunnel at {} with protocol {:?}", + local, + protocol + ); + let host_tunnel = + HostTunnel::new(name, iroh::SecretKey::generate(&mut rng), local, protocol); + config.host_tunnels.push(HostTunnelConfig { + name: host_tunnel.name().cloned(), + secret: host_tunnel.secret().clone(), + local_addr: host_tunnel.address(), + proto: host_tunnel.protocol(), + enabled: true, + }); + save_config(&args.config, &config)?; + tracing::info!("Host tunnel created and saved to config."); + } + Action::AddClientTunnel { + local, + protocol, + public_key, + name, + } => { + tracing::info!( + "Adding client tunnel to {} with protocol {:?}", + local, + protocol + ); + let client_tunnel = ClientTunnel::new(name, public_key, local, protocol); + config + .client_tunnels + .push(crate::config::ClientTunnelConfig { + name: client_tunnel.name().cloned(), + host_public: *client_tunnel.host_public_key(), + local_addr: *client_tunnel.address(), + proto: *client_tunnel.protocol(), + enabled: true, + }); + save_config(&args.config, &config)?; + tracing::info!("Client tunnel added and saved to config."); + } + }, + } + + // wait for ctrl-c + tokio::signal::ctrl_c().await?; + tracing::info!("Shutting down..."); + + Ok(()) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..208ca08 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,2 @@ +pub mod test_tcp_tunnel; +pub mod test_udp_tunnel; diff --git a/src/tests/test_tcp_tunnel.rs b/src/tests/test_tcp_tunnel.rs new file mode 100644 index 0000000..e6360a9 --- /dev/null +++ b/src/tests/test_tcp_tunnel.rs @@ -0,0 +1,566 @@ +use crate::{client::ClientTunnel, common::Protocol, host::HostTunnel}; +use iroh::SecretKey; +use rand::Rng; +use std::net::SocketAddr; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::{TcpListener, TcpStream}, + time::{Duration, timeout}, +}; + +/// Helper to create a test TCP echo server +async fn start_echo_server(addr: SocketAddr) -> tokio::task::JoinHandle<()> { + let listener = TcpListener::bind(addr).await.unwrap(); + + tokio::spawn(async move { + while let Ok((mut stream, _)) = listener.accept().await { + tokio::spawn(async move { + let mut buf = vec![0u8; 1024]; + loop { + match stream.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + if stream.write_all(&buf[..n]).await.is_err() { + break; + } + } + Err(_) => break, + } + } + }); + } + }) +} + +#[tokio::test] +async fn test_tunnel_creation() { + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let secret = SecretKey::generate(&mut rand::rng()); + + let host_tunnel = HostTunnel::new( + Some("test-host".to_string()), + secret, + echo_addr, + Protocol::Tcp, + ); + + // Start tunnel and connect to iroh network + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + let _stopped_tunnel = host_tunnel.stop().await; +} + +/// Test simple data exchange +#[tokio::test] +async fn test_data_exchange() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let _echo_server = start_echo_server(echo_addr).await; + + // Give server time to start + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host".to_string()), + secret, + echo_addr, + Protocol::Tcp, + ); + + let mut host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(client_addr).await.unwrap(); + let client_addr = listener.local_addr().unwrap(); + drop(listener); + + let client_tunnel = ClientTunnel::new( + Some("test-client".to_string()), + public_key, + client_addr, + Protocol::Tcp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // local connection to client tunnel's internal socket + let mut stream = timeout(Duration::from_secs(5), TcpStream::connect(client_addr)) + .await + .expect("Timeout connecting to client tunnel") + .expect("Failed to connect to client tunnel"); + + // Send data over tunnel + let test_data = rand::rng() + .sample_iter::(rand::distr::StandardUniform) + .take(1024) + .collect::>(); + stream + .write_all(&test_data) + .await + .expect("Failed to write data"); + + // Read echo data + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), stream.read_exact(&mut buf)) + .await + .expect("Timeout reading response"); + + read_result.expect("Failed to read response"); + + assert_eq!(buf, test_data, "Echoed data doesn't match sent data"); + assert_eq!(host_tunnel.num_connections().await, 1); + + let _stopped_client = client_tunnel.stop().await; + + // Ensure client is disconnected + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test multiple concurrent connections through the tunnel +#[tokio::test] +async fn test_multiple_concurrent_clients() { + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-multi".to_string()), + secret, + echo_addr, + Protocol::Tcp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(client_addr).await.unwrap(); + let client_addr = listener.local_addr().unwrap(); + drop(listener); + + let client_tunnel = ClientTunnel::new( + Some("test-client-multi".to_string()), + public_key, + client_addr, + Protocol::Tcp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + const NUM_CLIENTS: usize = 30; + let mut handles = Vec::new(); + + // Spawn multiple concurrent clients + for i in 0..NUM_CLIENTS { + let client_addr = client_addr.clone(); + let handle = tokio::spawn(async move { + let mut stream = timeout(Duration::from_secs(15), TcpStream::connect(client_addr)) + .await + .expect(&format!("Client {}: Timeout connecting", i)) + .expect(&format!("Client {}: Failed to connect", i)); + + // Each client sends unique data + let test_data = format!("Hello from client {}", i); + stream + .write_all(test_data.as_bytes()) + .await + .expect(&format!("Client {}: Failed to write", i)); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), stream.read_exact(&mut buf)) + .await + .expect(&format!("Client {}: Timeout reading", i)); + + read_result.expect(&format!("Client {}: Failed to read", i)); + + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Client {}: Echoed data doesn't match", + i + ); + }); + + handles.push(handle); + } + + for (i, handle) in handles.into_iter().enumerate() { + handle.await.expect(&format!("Client {} task panicked", i)); + } + + tokio::time::sleep(Duration::from_millis(500)).await; + + let _stopped_client = client_tunnel.stop().await; + let _stopped_host = host_tunnel.stop().await; +} + +/// Test bidirectional data flow with larger payloads +#[tokio::test] +async fn test_large_data_transfer() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-large".to_string()), + secret, + echo_addr, + Protocol::Tcp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(client_addr).await.unwrap(); + let client_addr = listener.local_addr().unwrap(); + drop(listener); + + let client_tunnel = ClientTunnel::new( + Some("test-client-large".to_string()), + public_key, + client_addr, + Protocol::Tcp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Connect to the client tunnel + let mut stream = timeout(Duration::from_secs(5), TcpStream::connect(client_addr)) + .await + .expect("Timeout connecting to client tunnel") + .expect("Failed to connect to client tunnel"); + + // Send large payload (100KB) + let test_data: Vec = (0..100_000).map(|i| (i % 256) as u8).collect(); + stream + .write_all(&test_data) + .await + .expect("Failed to write large data"); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(10), stream.read_exact(&mut buf)) + .await + .expect("Timeout reading large response"); + + read_result.expect("Failed to read large response"); + + assert_eq!(buf, test_data, "Large echoed data doesn't match sent data"); + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 1); + + let _stopped_client = client_tunnel.stop().await; + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test sequential data exchanges on the same connection +#[tokio::test] +async fn test_sequential_exchanges() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let _echo_server = start_echo_server(echo_addr).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-seq".to_string()), + secret, + echo_addr, + Protocol::Tcp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(client_addr).await.unwrap(); + let client_addr = listener.local_addr().unwrap(); + drop(listener); + + let client_tunnel = ClientTunnel::new( + Some("test-client-seq".to_string()), + public_key, + client_addr, + Protocol::Tcp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Connect once + let mut stream = timeout(Duration::from_secs(5), TcpStream::connect(client_addr)) + .await + .expect("Timeout connecting to client tunnel") + .expect("Failed to connect to client tunnel"); + + // Perform multiple sequential exchanges on the same connection + for i in 0..10 { + let test_data = format!("Message number {}", i); + + // Write + stream + .write_all(test_data.as_bytes()) + .await + .expect(&format!("Failed to write message {}", i)); + + // Read + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), stream.read_exact(&mut buf)) + .await + .expect(&format!("Timeout reading message {}", i)); + + read_result.expect(&format!("Failed to read message {}", i)); + + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Message {} doesn't match", + i + ); + } + + assert_eq!(host_tunnel.num_connections().await, 1); + let _stopped_client = client_tunnel.stop().await; + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test multiple different TCP tunnels running in parallel, each with multiple concurrent connections +#[tokio::test] +async fn test_multiple_tunnels_with_concurrent_connections() { + const NUM_TUNNELS: usize = 10; + const CONNECTIONS_PER_TUNNEL: usize = 10; + + let mut tunnel_handles = Vec::new(); + + // Create multiple tunnels in parallel + for tunnel_id in 0..NUM_TUNNELS { + let handle = tokio::spawn(async move { + // Start a local echo server for this tunnel + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(echo_addr).await.unwrap(); + let echo_addr = listener.local_addr().unwrap(); + drop(listener); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some(format!("test-host-{}", tunnel_id)), + secret, + echo_addr, + Protocol::Tcp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect(&format!("Failed to start host tunnel {}", tunnel_id)); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let listener = TcpListener::bind(client_addr).await.unwrap(); + let client_addr = listener.local_addr().unwrap(); + drop(listener); + + let client_tunnel = ClientTunnel::new( + Some(format!("test-client-{}", tunnel_id)), + public_key, + client_addr, + Protocol::Tcp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect(&format!("Failed to start client tunnel {}", tunnel_id)); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Spawn multiple concurrent connections for this tunnel + let mut connection_handles = Vec::new(); + + for conn_id in 0..CONNECTIONS_PER_TUNNEL { + let client_addr = client_addr.clone(); + let handle = tokio::spawn(async move { + let mut stream = + timeout(Duration::from_secs(10), TcpStream::connect(client_addr)) + .await + .expect(&format!( + "Tunnel {}, Connection {}: Timeout connecting", + tunnel_id, conn_id + )) + .expect(&format!( + "Tunnel {}, Connection {}: Failed to connect", + tunnel_id, conn_id + )); + + // Each connection sends 10 packets sequentially on the same TCP stream + const PACKETS_PER_CONNECTION: usize = 10; + + for packet_id in 0..PACKETS_PER_CONNECTION { + let test_data = format!( + "Tunnel {} - Connection {} - Packet {}", + tunnel_id, conn_id, packet_id + ); + + stream + .write_all(test_data.as_bytes()) + .await + .expect(&format!( + "Tunnel {}, Connection {}, Packet {}: Failed to write", + tunnel_id, conn_id, packet_id + )); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = + timeout(Duration::from_secs(5), stream.read_exact(&mut buf)) + .await + .expect(&format!( + "Tunnel {}, Connection {}, Packet {}: Timeout reading", + tunnel_id, conn_id, packet_id + )); + + read_result.expect(&format!( + "Tunnel {}, Connection {}, Packet {}: Failed to read", + tunnel_id, conn_id, packet_id + )); + + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Tunnel {}, Connection {}, Packet {}: Echoed data doesn't match", + tunnel_id, + conn_id, + packet_id + ); + } + }); + + connection_handles.push(handle); + } + + // Wait for all connections in this tunnel to complete + for (conn_id, handle) in connection_handles.into_iter().enumerate() { + handle.await.expect(&format!( + "Tunnel {}, Connection {} task panicked", + tunnel_id, conn_id + )); + } + + // Cleanup this tunnel + let _stopped_client = client_tunnel.stop().await; + let _stopped_host = host_tunnel.stop().await; + }); + + tunnel_handles.push(handle); + } + + // Wait for all tunnels to complete + for (tunnel_id, handle) in tunnel_handles.into_iter().enumerate() { + handle + .await + .expect(&format!("Tunnel {} task panicked", tunnel_id)); + } +} diff --git a/src/tests/test_udp_tunnel.rs b/src/tests/test_udp_tunnel.rs new file mode 100644 index 0000000..c8a2ee6 --- /dev/null +++ b/src/tests/test_udp_tunnel.rs @@ -0,0 +1,557 @@ +use crate::{client::ClientTunnel, common::Protocol, host::HostTunnel}; +use iroh::SecretKey; +use rand::Rng; +use std::net::SocketAddr; +use tokio::{ + net::UdpSocket, + time::{Duration, timeout}, +}; + +/// Helper to create a test UDP echo server +async fn start_echo_server(addr: SocketAddr) -> tokio::task::JoinHandle<()> { + let socket = UdpSocket::bind(addr).await.unwrap(); + + tokio::spawn(async move { + let mut buf = vec![0u8; 65536]; + loop { + match socket.recv_from(&mut buf).await { + Ok((n, peer_addr)) => { + if socket.send_to(&buf[..n], peer_addr).await.is_err() { + break; + } + } + Err(_) => break, + } + } + }) +} + +#[tokio::test] +async fn test_tunnel_creation() { + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let secret = SecretKey::generate(&mut rand::rng()); + + let host_tunnel = HostTunnel::new( + Some("test-host".to_string()), + secret, + echo_addr, + Protocol::Udp, + ); + + // Start tunnel and connect to iroh network + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + let _stopped_tunnel = host_tunnel.stop().await; +} + +/// Test simple data exchange +#[tokio::test] +async fn test_data_exchange() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let _echo_server = start_echo_server(echo_addr).await; + + // Give server time to start + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host".to_string()), + secret, + echo_addr, + Protocol::Udp, + ); + + let mut host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(client_addr).await.unwrap(); + let client_addr = socket.local_addr().unwrap(); + drop(socket); + + let client_tunnel = ClientTunnel::new( + Some("test-client".to_string()), + public_key, + client_addr, + Protocol::Udp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Create UDP socket to send to client tunnel's internal socket + let send_socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + + // Send data over tunnel + let test_data = rand::rng() + .sample_iter::(rand::distr::StandardUniform) + .take(1024) + .collect::>(); + + send_socket + .send_to(&test_data, client_addr) + .await + .expect("Failed to send data"); + + // Read echo data + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), send_socket.recv_from(&mut buf)) + .await + .expect("Timeout reading response"); + + let (n, _) = read_result.expect("Failed to read response"); + + assert_eq!(n, test_data.len()); + assert_eq!(buf, test_data, "Echoed data doesn't match sent data"); + assert_eq!(host_tunnel.num_connections().await, 1); + + let _stopped_client = client_tunnel.stop().await; + + // Ensure client is disconnected + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test multiple concurrent connections through the tunnel +#[tokio::test] +async fn test_multiple_concurrent_clients() { + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-multi".to_string()), + secret, + echo_addr, + Protocol::Udp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(client_addr).await.unwrap(); + let client_addr = socket.local_addr().unwrap(); + drop(socket); + + let client_tunnel = ClientTunnel::new( + Some("test-client-multi".to_string()), + public_key, + client_addr, + Protocol::Udp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(5000)).await; + + const NUM_CLIENTS: usize = 30; + let mut handles = Vec::new(); + + // Spawn multiple concurrent clients + for i in 0..NUM_CLIENTS { + let client_addr = client_addr.clone(); + let handle = tokio::spawn(async move { + let socket = UdpSocket::bind("127.0.0.1:0") + .await + .expect(&format!("Client {}: Failed to bind socket", i)); + + // Each client sends unique data + let test_data = format!("Hello from client {}", i); + socket + .send_to(test_data.as_bytes(), client_addr) + .await + .expect(&format!("Client {}: Failed to send", i)); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), socket.recv_from(&mut buf)) + .await + .expect(&format!("Client {}: Timeout reading", i)); + + let (n, _) = read_result.expect(&format!("Client {}: Failed to read", i)); + + assert_eq!( + n, + test_data.len(), + "Client {}: Received wrong amount of data", + i + ); + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Client {}: Echoed data doesn't match", + i + ); + }); + + handles.push(handle); + } + + for (i, handle) in handles.into_iter().enumerate() { + handle.await.expect(&format!("Client {} task panicked", i)); + } + + tokio::time::sleep(Duration::from_millis(500)).await; + + let _stopped_client = client_tunnel.stop().await; + let _stopped_host = host_tunnel.stop().await; +} + +/// Test bidirectional data flow with larger payloads +#[tokio::test] +async fn test_large_data_transfer() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-large".to_string()), + secret, + echo_addr, + Protocol::Udp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(client_addr).await.unwrap(); + let client_addr = socket.local_addr().unwrap(); + drop(socket); + + let client_tunnel = ClientTunnel::new( + Some("test-client-large".to_string()), + public_key, + client_addr, + Protocol::Udp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Create UDP socket + let send_socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + + // Send large payload (9KB - safe size that works reliably on all systems) + // for some reason packets above ~10KB are dropped even locally (linux) + // TODO: figure out if this needs to be fixed + let test_data: Vec = (0..9_000).map(|i| (i % 256) as u8).collect(); + send_socket + .send_to(&test_data, client_addr) + .await + .expect("Failed to send large data"); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(10), send_socket.recv_from(&mut buf)) + .await + .expect("Timeout reading large response"); + + let (n, _) = read_result.expect("Failed to read large response"); + + assert_eq!(n, test_data.len(), "Received wrong amount of data"); + assert_eq!(buf, test_data, "Large echoed data doesn't match sent data"); + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 1); + + let _stopped_client = client_tunnel.stop().await; + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test sequential data exchanges on the same socket +#[tokio::test] +async fn test_sequential_exchanges() { + // Start a local echo server + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let _echo_server = start_echo_server(echo_addr).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some("test-host-seq".to_string()), + secret, + echo_addr, + Protocol::Udp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect("Failed to start host tunnel"); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(client_addr).await.unwrap(); + let client_addr = socket.local_addr().unwrap(); + drop(socket); + + let client_tunnel = ClientTunnel::new( + Some("test-client-seq".to_string()), + public_key, + client_addr, + Protocol::Udp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect("Failed to start client tunnel"); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Create UDP socket for sending + let send_socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + + // Perform multiple sequential exchanges on the same socket + for i in 0..10 { + let test_data = format!("Message number {}", i); + + // Send + send_socket + .send_to(test_data.as_bytes(), client_addr) + .await + .expect(&format!("Failed to send message {}", i)); + + // Read + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), send_socket.recv_from(&mut buf)) + .await + .expect(&format!("Timeout reading message {}", i)); + + let (n, _) = read_result.expect(&format!("Failed to read message {}", i)); + + assert_eq!( + n, + test_data.len(), + "Message {}: received wrong amount of data", + i + ); + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Message {} doesn't match", + i + ); + } + + assert_eq!(host_tunnel.num_connections().await, 1); + let _stopped_client = client_tunnel.stop().await; + + tokio::time::sleep(Duration::from_millis(100)).await; + assert_eq!(host_tunnel.num_connections().await, 0); + + let _stopped_host = host_tunnel.stop().await; +} + +/// Test multiple different UDP tunnels running in parallel, each with multiple concurrent connections +#[tokio::test] +async fn test_multiple_tunnels_with_concurrent_connections() { + const NUM_TUNNELS: usize = 10; + const CONNECTIONS_PER_TUNNEL: usize = 10; + + let mut tunnel_handles = Vec::new(); + + // Create multiple tunnels in parallel + for tunnel_id in 0..NUM_TUNNELS { + let handle = tokio::spawn(async move { + // Start a local echo server for this tunnel + let echo_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(echo_addr).await.unwrap(); + let echo_addr = socket.local_addr().unwrap(); + drop(socket); + + let _echo_server = start_echo_server(echo_addr).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + // Create and start host tunnel + let secret = SecretKey::generate(&mut rand::rng()); + let public_key = secret.public(); + + let host_tunnel = HostTunnel::new( + Some(format!("test-host-{}", tunnel_id)), + secret, + echo_addr, + Protocol::Udp, + ); + + let host_tunnel = host_tunnel + .start() + .await + .expect(&format!("Failed to start host tunnel {}", tunnel_id)); + + tokio::time::sleep(Duration::from_millis(200)).await; + + // Create and start client tunnel + let client_addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = UdpSocket::bind(client_addr).await.unwrap(); + let client_addr = socket.local_addr().unwrap(); + drop(socket); + + let client_tunnel = ClientTunnel::new( + Some(format!("test-client-{}", tunnel_id)), + public_key, + client_addr, + Protocol::Udp, + ); + + let client_tunnel = client_tunnel + .start() + .await + .expect(&format!("Failed to start client tunnel {}", tunnel_id)); + + tokio::time::sleep(Duration::from_millis(500)).await; + + // Spawn multiple concurrent connections for this tunnel + let mut connection_handles = Vec::new(); + + for conn_id in 0..CONNECTIONS_PER_TUNNEL { + let client_addr = client_addr.clone(); + let handle = tokio::spawn(async move { + let socket = UdpSocket::bind("127.0.0.1:0").await.expect(&format!( + "Tunnel {}, Connection {}: Failed to bind socket", + tunnel_id, conn_id + )); + + let test_data = + format!("Tunnel {} - Connection {} - Test Data", tunnel_id, conn_id); + socket + .send_to(test_data.as_bytes(), client_addr) + .await + .expect(&format!( + "Tunnel {}, Connection {}: Failed to send", + tunnel_id, conn_id + )); + + // Read echoed data back + let mut buf = vec![0u8; test_data.len()]; + let read_result = timeout(Duration::from_secs(5), socket.recv_from(&mut buf)) + .await + .expect(&format!( + "Tunnel {}, Connection {}: Timeout reading", + tunnel_id, conn_id + )); + + let (n, _) = read_result.expect(&format!( + "Tunnel {}, Connection {}: Failed to read", + tunnel_id, conn_id + )); + + assert_eq!( + n, + test_data.len(), + "Tunnel {}, Connection {}: received wrong amount of data", + tunnel_id, + conn_id + ); + assert_eq!( + String::from_utf8_lossy(&buf), + test_data, + "Tunnel {}, Connection {}: Echoed data doesn't match", + tunnel_id, + conn_id + ); + }); + + connection_handles.push(handle); + } + + // Wait for all connections in this tunnel to complete + for (conn_id, handle) in connection_handles.into_iter().enumerate() { + handle.await.expect(&format!( + "Tunnel {}, Connection {} task panicked", + tunnel_id, conn_id + )); + } + + // Cleanup this tunnel + let _stopped_client = client_tunnel.stop().await; + let _stopped_host = host_tunnel.stop().await; + }); + + tunnel_handles.push(handle); + } + + // Wait for all tunnels to complete + for (tunnel_id, handle) in tunnel_handles.into_iter().enumerate() { + handle + .await + .expect(&format!("Tunnel {} task panicked", tunnel_id)); + } +}