diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c3209c9a4..1c54bb6c7 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -33,14 +33,9 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ key: coverage-${{ hashFiles('**/Cargo.toml') }}-${{ matrix.os }} - - uses: ilammy/setup-nasm@v1 + - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - version: "23.x" - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Cargo test and coverage uses: clechasseur/rs-cargo@v4 diff --git a/Cargo.lock b/Cargo.lock index a998e576b..603c026a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,7 +248,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -259,7 +259,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -364,13 +364,29 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive 0.5.1", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + [[package]] name = "asn1-rs" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" dependencies = [ - "asn1-rs-derive", + "asn1-rs-derive 0.6.0", "asn1-rs-impl", "displaydoc", "nom 7.1.3", @@ -380,6 +396,18 @@ dependencies = [ "time", ] +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "asn1-rs-derive" version = "0.6.0" @@ -443,6 +471,37 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-http-codec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "096146020b08dbc4587685b0730a7ba905625af13c65f8028035cdfd69573c91" +dependencies = [ + "anyhow", + "futures", + "http", + "httparse", + "log", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + [[package]] name = "async-lock" version = "3.4.2" @@ -454,6 +513,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + [[package]] name = "async-object-pool" version = "0.2.0" @@ -492,6 +562,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "async-web-client" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37381fb4fad3cd9b579628c21a58f528ef029d1f072d10f16cb9431aa2236d29" +dependencies = [ + "async-http-codec", + "async-net", + "futures", + "futures-rustls", + "http", + "lazy_static", + "log", + "rustls-pki-types", + "thiserror 1.0.69", + "webpki-roots 0.26.11", +] + [[package]] name = "async_executors" version = "0.7.0" @@ -625,6 +713,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-extra" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-core", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-macros" version = "0.5.0" @@ -636,6 +746,23 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "axum-server" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1df331683d982a0b9492b38127151e6453639cd34926eb9c07d4cd8c6d22bfc" +dependencies = [ + "bytes", + "either", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "tokio", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -1355,6 +1482,7 @@ dependencies = [ "md-5", "memory-stats", "mockall", + "moka", "murmur3", "network-interface", "opentelemetry", @@ -1366,8 +1494,8 @@ dependencies = [ "prost-build", "protox", "public-suffix", - "quinn", - "quinn-proto", + "quinn 0.11.9", + "quinn-proto 0.11.14", "rand 0.9.2", "rand_chacha 0.10.0", "regex", @@ -1413,6 +1541,7 @@ dependencies = [ "tracing-subscriber", "tracing-test", "tuic-core", + "tuic-server", "tun-rs", "unix-udp-sock", "url", @@ -1420,7 +1549,7 @@ dependencies = [ "watfaq-dns", "watfaq-netstack", "watfaq-rustls", - "webpki-roots", + "webpki-roots 1.0.6", "windows 0.62.2", "zip", "zstd", @@ -2175,13 +2304,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs 0.6.2", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "der-parser" version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", "cookie-factory", "displaydoc", "nom 7.1.3", @@ -2217,7 +2360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caef6056a5788d05d173cdc3c562ac28ae093828f851f69378b74e4e3d578e41" dependencies = [ "heck", - "indexmap 2.13.1", + "indexmap 1.9.3", "itertools 0.14.0", "proc-macro-crate", "proc-macro2", @@ -2373,7 +2516,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2774,7 +2917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2838,6 +2981,19 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastbloom" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef975e30683b2d965054bb0a836f8973857c4ebf6acf274fe46617cd285060d8" +dependencies = [ + "foldhash 0.2.0", + "libm", + "portable-atomic", + "rand 0.9.2", + "siphasher", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -2876,12 +3032,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ "atomic 0.6.1", + "pear", "serde", + "serde_yaml", "toml 0.8.23", "uncased", "version_check", ] +[[package]] +name = "figment-json5" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f6982da09e166efe7dc3c5cf1fe01ef85419733eb188c0df0b571eda9e8a81" +dependencies = [ + "figment", + "json5 0.4.1", + "serde", +] + [[package]] name = "filetime" version = "0.2.27" @@ -2970,6 +3139,16 @@ dependencies = [ "futures-core", ] +[[package]] +name = "fs-err" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fde052dbfc920003cfd2c8e2c6e6d4cc7c1091538c3a24226cec0665ab08c0" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "fs-mistrust" version = "0.13.2" @@ -3070,7 +3249,10 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ + "fastrand", "futures-core", + "futures-io", + "parking", "pin-project-lite", ] @@ -3343,7 +3525,7 @@ dependencies = [ "bytes", "futures", "h3 0.0.7", - "quinn", + "quinn 0.11.9", "tokio", "tokio-util", ] @@ -3357,7 +3539,7 @@ dependencies = [ "bytes", "futures", "h3 0.0.8", - "quinn", + "quinn 0.11.9", "tokio", "tokio-util", ] @@ -3486,6 +3668,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -3541,7 +3729,7 @@ dependencies = [ "ipnet", "once_cell", "pin-project-lite", - "quinn", + "quinn 0.11.9", "rand 0.9.2", "ring", "rustls", @@ -3569,7 +3757,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "quinn", + "quinn 0.11.9", "rand 0.9.2", "resolv-conf", "rustls", @@ -3860,7 +4048,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", + "webpki-roots 1.0.6", ] [[package]] @@ -4090,6 +4278,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "inotify" version = "0.11.1" @@ -4180,6 +4374,12 @@ dependencies = [ "rustversion", ] +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + [[package]] name = "ip_network_table-deps-treebitmap" version = "0.5.0" @@ -4280,7 +4480,7 @@ dependencies = [ "libc", "socket2 0.6.3", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4359,6 +4559,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -4409,6 +4639,27 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "json5" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733a844dbd6fef128e98cb4487b887cb55454d92cd9994b1bafe004fabbe670c" +dependencies = [ + "serde", + "ucd-trie", +] + [[package]] name = "kameo" version = "0.19.2" @@ -4873,10 +5124,13 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" dependencies = [ + "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", "equivalent", + "event-listener", + "futures-util", "parking_lot", "portable-atomic", "smallvec", @@ -5216,7 +5470,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5491,13 +5745,22 @@ 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 0.6.2", +] + [[package]] name = "oid-registry" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", ] [[package]] @@ -5852,6 +6115,39 @@ dependencies = [ "hmac 0.13.0", ] +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -6110,6 +6406,20 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -6364,6 +6674,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.14.3" @@ -6469,7 +6792,19 @@ dependencies = [ "derive-deftly", "libc", "paste", - "thiserror 2.0.18", + "thiserror 1.0.69", +] + +[[package]] +name = "qlog" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13932f4f3e6b912a43b3af40a3174614b8408c85059b79ef23fe1b3c87e5d04a" +dependencies = [ + "humantime", + "serde", + "serde_json", + "serde_with", ] [[package]] @@ -6482,8 +6817,28 @@ dependencies = [ "cfg_aliases", "futures-io", "pin-project-lite", - "quinn-proto", - "quinn-udp", + "quinn-proto 0.11.14", + "quinn-udp 0.5.14", + "rustc-hash 2.1.2", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn" +version = "0.12.0" +source = "git+https://github.com/Tipuch/quinn.git?branch=bbrv3#ce60e5b5c115db2a6053f4e0ca7fc52103cb76b9" +dependencies = [ + "bytes", + "cfg_aliases", + "futures-io", + "pin-project-lite", + "quinn-proto 0.12.0", + "quinn-udp 0.6.1", "rustc-hash 2.1.2", "rustls", "socket2 0.6.3", @@ -6493,6 +6848,16 @@ dependencies = [ "web-time", ] +[[package]] +name = "quinn-congestions" +version = "0.1.0" +source = "git+https://github.com/proxy-rs/quinn-congestions.git#2bdc356f57a7fc3ae1a49ab963a221f42d61012f" +dependencies = [ + "quinn 0.12.0", + "quinn-proto 0.12.0", + "rand 0.9.2", +] + [[package]] name = "quinn-jls" version = "0.3.3" @@ -6535,6 +6900,31 @@ dependencies = [ "web-time", ] +[[package]] +name = "quinn-proto" +version = "0.12.0" +source = "git+https://github.com/Tipuch/quinn.git?branch=bbrv3#ce60e5b5c115db2a6053f4e0ca7fc52103cb76b9" +dependencies = [ + "aws-lc-rs", + "bytes", + "fastbloom", + "getrandom 0.3.4", + "lru-slab", + "qlog", + "rand 0.9.2", + "rand_pcg", + "ring", + "rustc-hash 2.1.2", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier 0.7.0", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + [[package]] name = "quinn-proto-jls" version = "0.3.3" @@ -6570,6 +6960,18 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "quinn-udp" +version = "0.6.1" +source = "git+https://github.com/Tipuch/quinn.git?branch=bbrv3#ce60e5b5c115db2a6053f4e0ca7fc52103cb76b9" +dependencies = [ + "cfg_aliases", + "libc", + "socket2 0.6.3", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quinn-udp-jls" version = "0.3.3" @@ -6728,6 +7130,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand_pcg" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b48ac3f7ffaab7fac4d2376632268aa5f89abdb55f7ebf8f4d11fffccb2320f7" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "rayon" version = "1.11.0" @@ -6748,16 +7159,31 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "aws-lc-rs", + "pem", + "rustls-pki-types", + "time", + "yasna", +] + [[package]] name = "rcgen" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e" dependencies = [ + "aws-lc-rs", + "pem", "ring", "rustls-pki-types", "time", - "x509-parser", + "x509-parser 0.18.1", "yasna", ] @@ -6881,7 +7307,7 @@ dependencies = [ "log", "percent-encoding", "pin-project-lite", - "quinn", + "quinn 0.11.9", "rustls", "rustls-pki-types", "serde", @@ -6897,7 +7323,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 1.0.6", ] [[package]] @@ -6922,10 +7348,10 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "quinn", + "quinn 0.11.9", "rustls", "rustls-pki-types", - "rustls-platform-verifier", + "rustls-platform-verifier 0.6.2", "sync_wrapper", "tokio", "tokio-rustls", @@ -7256,7 +7682,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7275,6 +7701,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-acme" +version = "0.15.1" +source = "git+https://github.com/rust-proxy/rustls-acme?branch=feat%2Fip#27c0b4ebfe1e0ec4711e8e63eface99b8b6bf2c8" +dependencies = [ + "async-io", + "async-trait", + "async-web-client", + "aws-lc-rs", + "base64 0.22.1", + "blocking", + "chrono", + "futures", + "futures-rustls", + "http", + "log", + "pem", + "rcgen 0.13.2", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tower-service", + "webpki-roots 1.0.6", + "x509-parser 0.16.0", +] + [[package]] name = "rustls-jls" version = "1.3.1" @@ -7329,7 +7783,7 @@ checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.21.1", "log", "once_cell", "rustls", @@ -7339,7 +7793,28 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.10", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.52.0", ] [[package]] @@ -8011,7 +8486,7 @@ dependencies = [ "notify", "quinn-jls", "quinn-proto-jls", - "rcgen", + "rcgen 0.14.7", "ring", "rustls", "rustls-jls", @@ -8026,7 +8501,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "webpki-roots", + "webpki-roots 1.0.6", "yaml-rust2", ] @@ -8170,6 +8645,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -8302,7 +8793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8679,7 +9170,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8689,7 +9180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -9014,10 +9505,13 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ + "indexmap 2.13.1", "serde_core", "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", "toml_writer", + "winnow 1.0.1", ] [[package]] @@ -9742,7 +10236,7 @@ dependencies = [ "base64ct", "ctr 0.9.2", "curve25519-dalek 4.1.3", - "der-parser", + "der-parser 10.0.0", "derive-deftly", "derive_more", "digest 0.10.7", @@ -10761,7 +11255,7 @@ dependencies = [ "tokio", "tokio-rustls", "url", - "webpki-roots", + "webpki-roots 1.0.6", ] [[package]] @@ -10834,20 +11328,71 @@ dependencies = [ [[package]] name = "tuic-core" -version = "1.6.7" -source = "git+https://github.com/Itsusinn/tuic.git?tag=v1.6.7#4843d04b1584f49b503e344e2a96041e16fe8938" +version = "1.7.2" +source = "git+https://github.com/Itsusinn/tuic.git?tag=v1.7.2#18b74bcf11fe33caf9dcfc9e2d6685c5230a2e0a" dependencies = [ "bytes", "eyre", "futures-util", "parking_lot", - "quinn", + "quinn 0.12.0", + "register-count", + "serde", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "tuic-server" +version = "1.7.2" +source = "git+https://github.com/Itsusinn/tuic.git?tag=v1.7.2#18b74bcf11fe33caf9dcfc9e2d6685c5230a2e0a" +dependencies = [ + "arc-swap", + "aws-lc-rs", + "axum", + "axum-extra", + "axum-server", + "bytes", + "clap", + "derive_more", + "educe 0.6.0", + "eyre", + "figment", + "figment-json5", + "humantime", + "humantime-serde", + "ip_network", + "ipnet", + "json5 1.3.1", + "moka", + "pest", + "pest_derive", + "quinn 0.12.0", + "quinn-congestions", + "rand 0.10.0", + "rcgen 0.14.7", + "regex", "register-count", + "rustls", + "rustls-acme", + "rustls-pemfile", "serde", + "serde_json", + "sha2 0.11.0", + "socket2 0.6.3", "thiserror 2.0.18", + "time", "tokio", + "tokio-stream", + "tokio-util", + "toml 1.1.2+spec-1.1.0", "tracing", + "tracing-subscriber", + "tuic-core", "uuid", + "x509-parser 0.18.1", ] [[package]] @@ -11096,7 +11641,7 @@ dependencies = [ "rustls-pki-types", "ureq-proto", "utf8-zero", - "webpki-roots", + "webpki-roots 1.0.6", ] [[package]] @@ -11386,7 +11931,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", - "webpki-roots", + "webpki-roots 1.0.6", ] [[package]] @@ -11462,6 +12007,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + [[package]] name = "webpki-roots" version = "1.0.6" @@ -11499,7 +12053,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -12104,18 +12658,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs 0.6.2", + "data-encoding", + "der-parser 9.0.0", + "lazy_static", + "nom 7.1.3", + "oid-registry 0.7.1", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + [[package]] name = "x509-parser" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", + "aws-lc-rs", "data-encoding", - "der-parser", + "der-parser 10.0.0", "lazy_static", "nom 7.1.3", - "oid-registry", + "oid-registry 0.8.1", "ring", "rusticata-macros", "thiserror 2.0.18", @@ -12143,6 +12715,12 @@ dependencies = [ "hashlink", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" diff --git a/clash-lib/Cargo.toml b/clash-lib/Cargo.toml index 69b018e7d..a88390c4e 100644 --- a/clash-lib/Cargo.toml +++ b/clash-lib/Cargo.toml @@ -173,7 +173,7 @@ arti-client = { version = "0.39", optional = true, default-features = false, fea tor-rtcompat = { version = "0.39", optional = true, default-features = false } # tuic -tuic-core= { tag = "v1.6.7", optional = true, git = "https://github.com/Itsusinn/tuic.git", features = ["async_marshal", "marshal", "model"] } +tuic-core= { tag = "v1.7.2", optional = true, git = "https://github.com/Itsusinn/tuic.git", features = ["async_marshal", "marshal", "model"] } register-count = { version = "0.1", optional = true } quinn = { version = "0.11", default-features = false, features = ["futures-io", "runtime-tokio", "rustls"] } @@ -221,6 +221,8 @@ tracing-test = "0.2" http-body-util = "0.1" reqwest = { version = "0.13", features = ["socks"] } sysinfo = { version = "0.38", features = ["network"]} +moka = { version = "0.12", features = ["future"] } +tuic-server = { tag = "v1.7.2", git = "https://github.com/Itsusinn/tuic.git" } [build-dependencies] prost-build = "0.14" diff --git a/clash-lib/src/proxy/tuic/mod.rs b/clash-lib/src/proxy/tuic/mod.rs index 17b9cbb37..242632a0e 100644 --- a/clash-lib/src/proxy/tuic/mod.rs +++ b/clash-lib/src/proxy/tuic/mod.rs @@ -409,89 +409,28 @@ impl TuicDatagramOutbound { } } -#[cfg(all(test, docker_test))] +#[cfg(test)] +pub(crate) mod test_utils; + +#[cfg(test)] mod tests { - use std::io::Write; + use std::{sync::Arc, time::Duration}; - use super::super::utils::test_utils::{ - consts::*, docker_runner::DockerTestRunner, + use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpListener, }; + + use super::{test_utils::TuicServerProcess, *}; use crate::{ - proxy::utils::{ - GLOBAL_DIRECT_CONNECTOR, - test_utils::{ - Suite, - config_helper::test_config_base_dir, - docker_runner::{DockerTestRunnerBuilder, alloc_docker_port}, - run_test_suites_and_cleanup, - }, - }, - tests::initialize, + proxy::utils::{GLOBAL_DIRECT_CONNECTOR, test_utils::noop::NoopResolver}, + session::Session, }; - use super::*; - - const TUIC_SERVER_CONFIG: &str = r#"server = "0.0.0.0:10002" - -data_dir = "" - -zero_rtt_handshake = false -dual_stack = false - -acl = ''' -direct 0.0.0.0/0 -direct ::/0 -''' - -[users] -00000000-0000-0000-0000-000000000001 = "passwd" - -[tls] -certificate = "/opt/tuic/fullchain.pem" -private_key = "/opt/tuic/privkey.pem" -alpn = ["h3"] - -[outbound.default] -type = "direct" -ip_mode = "auto" -"#; - - async fn get_tuic_runner(host_port: u16) -> anyhow::Result { - let test_config_dir = test_config_base_dir(); - let cert = test_config_dir.join("certs/example.org.pem"); - let key = test_config_dir.join("certs/example.org-key.pem"); - - let mut tmp = tempfile::NamedTempFile::new()?; - tmp.write_all(TUIC_SERVER_CONFIG.as_bytes())?; - - let result = DockerTestRunnerBuilder::new() - .image(IMAGE_TUIC) - .mounts(&[ - (tmp.path().to_str().unwrap(), "/etc/tuic/config.json"), - (cert.to_str().unwrap(), "/opt/tuic/fullchain.pem"), - (key.to_str().unwrap(), "/opt/tuic/privkey.pem"), - ]) - .env(&["TUIC_FORCE_TOML=1"]) - .host_port(host_port, 10002) - .build() - .await; - drop(tmp); - result - } - - fn gen_options( - container_ip: Option, - host_port: u16, - skip_cert_verify: bool, - ) -> anyhow::Result { - let port = if container_ip.is_some() { - 10002 - } else { - host_port - }; + fn gen_options(port: u16) -> anyhow::Result { Ok(HandlerOptions { name: "test-tuic".to_owned(), - server: container_ip.unwrap_or(LOCAL_ADDR.to_owned()), + server: "127.0.0.1".to_owned(), port, common_opts: Default::default(), uuid: "00000000-0000-0000-0000-000000000001".parse()?, @@ -506,9 +445,9 @@ ip_mode = "auto" congestion_controller: CongestionControl::Bbr, max_udp_relay_packet_size: 1500, max_open_stream: VarInt::from_u64(32)?, - ip: None, - skip_cert_verify, - sni: Some("example.org".to_owned()), + ip: Some("127.0.0.1".to_owned()), + skip_cert_verify: true, + sni: Some("localhost".to_owned()), gc_interval: Duration::from_millis(3000), gc_lifetime: Duration::from_millis(15000), send_window: 8 * 1024 * 1024 * 2, @@ -516,41 +455,124 @@ ip_mode = "auto" }) } + /// TCP ping-pong test: start an echo server, connect through tuic, send + /// "hello" and verify we receive "world" back. #[tokio::test] - async fn test_tuic_skip_cert_verify() -> anyhow::Result<()> { - initialize(); - let host_port = alloc_docker_port(); - - let container = get_tuic_runner(host_port).await?; - let opts = gen_options(container.container_ip(), host_port, true)?; + async fn test_tuic_ping_pong_tcp() -> anyhow::Result<()> { + crate::tests::initialize(); + let server = TuicServerProcess::start().await?; + let port = server.port(); + + // Start a local echo server as the target + let listener = TcpListener::bind("127.0.0.1:0").await?; + let target_port = listener.local_addr()?.port(); + + let target_handle = tokio::spawn(async move { + let (mut stream, _) = listener.accept().await.unwrap(); + let mut buf = vec![0u8; 5]; + for _ in 0..10 { + stream.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"hello"); + stream.write_all(b"world").await.unwrap(); + stream.flush().await.unwrap(); + } + }); + let opts = gen_options(port)?; let handler = Arc::new(Handler::new(opts)); handler .register_connector(GLOBAL_DIRECT_CONNECTOR.clone()) .await; - run_test_suites_and_cleanup(handler, container, Suite::all()).await + + let resolver = Arc::new(NoopResolver); + + let session = Session { + network: crate::session::Network::Tcp, + typ: crate::session::Type::Socks5, + source: "127.0.0.1:54321".parse()?, + destination: format!("127.0.0.1:{target_port}").parse()?, + resolved_ip: None, + so_mark: None, + iface: None, + asn: None, + traffic_stats: None, + inbound_user: None, + }; + + let mut stream = handler.connect_stream(&session, resolver).await?; + + for _ in 0..10 { + stream.write_all(b"hello").await?; + stream.flush().await?; + let mut buf = vec![0u8; 5]; + stream.read_exact(&mut buf).await?; + assert_eq!(&buf, b"world"); + } + + target_handle.await?; + Ok(()) } + /// Verify that connecting with an invalid password fails. #[tokio::test] - async fn test_tuic_cert_verify_expect_fail() -> anyhow::Result<()> { - initialize(); - let host_port = alloc_docker_port(); - - let container = get_tuic_runner(host_port).await?; + async fn test_tuic_auth_failure() -> anyhow::Result<()> { + crate::tests::initialize(); + let server = TuicServerProcess::start().await?; + let port = server.port(); + + // Start a local TCP target so failure cannot be blamed on an + // unreachable upstream. + let listener = TcpListener::bind("127.0.0.1:0").await?; + let target_port = listener.local_addr()?.port(); + let _target_handle = tokio::spawn(async move { + // Accept one connection, then idle — the test should never + // actually reach this point because auth fails first. + if let Ok((mut stream, _)) = listener.accept().await { + let mut buf = [0u8; 5]; + while stream.read_exact(&mut buf).await.is_ok() { + stream.write_all(b"world").await.ok(); + } + } + }); - let opts = gen_options(container.container_ip(), host_port, false)?; + let mut opts = gen_options(port)?; + opts.password = "wrong_password".into(); let handler = Arc::new(Handler::new(opts)); handler .register_connector(GLOBAL_DIRECT_CONNECTOR.clone()) .await; - let res = - run_test_suites_and_cleanup(handler, container, Suite::all()).await; - assert!(res.is_err()); - assert!(res.unwrap_err().to_string().contains( - "the cryptographic handshake failed: error 45: invalid peer \ - certificate: certificate expired" - )); + + let resolver = Arc::new(NoopResolver); + + let session = Session { + network: crate::session::Network::Tcp, + typ: crate::session::Type::Socks5, + source: "127.0.0.1:54321".parse()?, + destination: format!("127.0.0.1:{target_port}").parse()?, + resolved_ip: None, + so_mark: None, + iface: None, + asn: None, + traffic_stats: None, + inbound_user: None, + }; + + let result = handler.connect_stream(&session, resolver).await; + // The stream connect may succeed initially (auth is async), but + // reading/writing should fail after the server rejects authentication. + if let Ok(mut stream) = result { + let mut buf = [0u8; 5]; + // Give the server time to process auth and close + tokio::time::sleep(Duration::from_secs(1)).await; + let write_result = stream.write_all(b"hello").await; + let read_result = stream.read_exact(&mut buf).await; + assert!( + write_result.is_err() || read_result.is_err(), + "expected IO error after auth failure, but both read and write \ + succeeded" + ); + } Ok(()) } } diff --git a/clash-lib/src/proxy/tuic/test_utils.rs b/clash-lib/src/proxy/tuic/test_utils.rs new file mode 100644 index 000000000..3b8c1678a --- /dev/null +++ b/clash-lib/src/proxy/tuic/test_utils.rs @@ -0,0 +1,131 @@ +use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; + +use tokio::sync::oneshot; + +/// A running tuic-server instance that cleans up on drop. +pub struct TuicServerProcess { + handle: Option>, + port: u16, +} + +impl TuicServerProcess { + /// Start a tuic-server instance on a random port. + pub async fn start() -> anyhow::Result { + // We use a channel to receive the actual bound port from the task. + let (port_tx, port_rx) = oneshot::channel(); + + let (ready_tx, ready_rx) = oneshot::channel::>(); + + let handle = tokio::spawn(async move { + let sock = std::net::UdpSocket::bind("127.0.0.1:0") + .expect("failed to allocate a free port"); + let port = sock.local_addr().unwrap().port(); + let server_addr: SocketAddr = + format!("127.0.0.1:{port}").parse().unwrap(); + drop(sock); // socket released; tuic-server's init will re-bind + + let _ = port_tx.send(port); + + let cfg = tuic_server::Config { + server: server_addr, + log_level: tuic_server::config::LogLevel::Info, + users: HashMap::from([( + "00000000-0000-0000-0000-000000000001".parse().unwrap(), + "passwd".into(), + )]), + tls: tuic_server::config::TlsConfig { + self_sign: true, + hostname: "localhost".into(), + alpn: vec!["h3".into()], + ..Default::default() + }, + zero_rtt_handshake: false, + dual_stack: false, + outbound: tuic_server::config::OutboundConfig { + default: tuic_server::config::OutboundRule { + kind: "direct".into(), + ..Default::default() + }, + named: HashMap::new(), + }, + acl: vec![], + udp_relay_ipv6: false, + experimental: tuic_server::config::ExperimentalConfig { + drop_loopback: false, + drop_private: false, + }, + ..Default::default() + }; + + let mut online_counter = HashMap::new(); + for (user, _) in cfg.users.iter() { + online_counter + .insert(user.to_owned(), std::sync::atomic::AtomicUsize::new(0)); + } + let mut traffic_stats = HashMap::new(); + for (user, _) in cfg.users.iter() { + traffic_stats.insert( + user.to_owned(), + ( + std::sync::atomic::AtomicUsize::new(0), + std::sync::atomic::AtomicUsize::new(0), + ), + ); + } + let capacity = cfg.users.len() as u64; + let ctx = Arc::new(tuic_server::AppContext { + cfg, + online_counter, + online_clients: moka::future::Cache::new(capacity), + traffic_stats, + }); + match tuic_server::server::Server::init(ctx).await { + Ok(server) => { + let _ = ready_tx.send(Ok(())); + server.start().await; + } + Err(e) => { + tracing::error!("tuic-server init failed: {e}"); + let _ = ready_tx.send(Err(anyhow::anyhow!("{e}"))); + } + } + }); + + // Wait for the server to be ready (or for init to fail). + let port = tokio::time::timeout(Duration::from_secs(5), port_rx) + .await + .map_err(|_| anyhow::anyhow!("tuic-server failed to report a port"))? + .map_err(|_| { + anyhow::anyhow!("tuic-server task panicked before reporting port") + })?; + + tokio::time::timeout(Duration::from_secs(30), ready_rx) + .await + .map_err(|_| { + anyhow::anyhow!( + "tuic-server failed to start on port {port} within 30s" + ) + })? + .map_err(|e| anyhow::anyhow!("tuic-server init failed: {e}"))??; + + tracing::info!("tuic-server started on port {port}"); + + Ok(Self { + handle: Some(handle), + port, + }) + } + + pub fn port(&self) -> u16 { + self.port + } +} + +impl Drop for TuicServerProcess { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + handle.abort(); + tracing::info!("tuic-server task aborted"); + } + } +} diff --git a/clash-lib/src/proxy/utils/test_utils/docker_utils/consts.rs b/clash-lib/src/proxy/utils/test_utils/docker_utils/consts.rs index e0aa90cf0..3a9796b77 100644 --- a/clash-lib/src/proxy/utils/test_utils/docker_utils/consts.rs +++ b/clash-lib/src/proxy/utils/test_utils/docker_utils/consts.rs @@ -17,8 +17,6 @@ pub const IMAGE_SOCKS5: &str = "v2fly/v2fly-core:v4.45.2"; #[cfg(all(feature = "ssh", docker_test))] pub const IMAGE_OPENSSH: &str = "docker.io/linuxserver/openssh-server:latest"; pub const IMAGE_HYSTERIA: &str = "tobyxdd/hysteria:latest"; -#[cfg(feature = "tuic")] -pub const IMAGE_TUIC: &str = "ghcr.io/itsusinn/tuic-server:latest"; #[cfg(feature = "shadowquic")] pub const IMAGE_SHADOWQUIC: &str = "ghcr.io/spongebob888/shadowquic:latest"; pub const IMAGE_SINGBOX: &str = "ghcr.io/sagernet/sing-box:v1.13.8";