diff --git a/.cargo/config.toml b/.cargo/config.toml index 13c84ae1..20711f1b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,4 +3,4 @@ target-dir = "target" rustflags = ["--cfg", "tokio_unstable"] [env] -RUST_LOG = { value = "clash=trace" } +RUST_LOG = { value = "clash=trace" } \ No newline at end of file diff --git a/.github/cliff.toml b/.github/cliff.toml index f37a1ad3..faa4dc04 100644 --- a/.github/cliff.toml +++ b/.github/cliff.toml @@ -20,13 +20,23 @@ body = """ - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ {% if commit.breaking %}[**breaking**] {% endif %}\ {{ commit.message | upper_first }}\ + {% if commit.remote.username %} @{{ commit.remote.username }}{%- endif -%}\ {% endfor %} {% endfor %}\n """ # template for the changelog footer footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + ## New Contributors ❤️ +{% endif %}\ {% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} - * @{{ contributor.username }} made their first contribution in #{{ contributor.pr_number }} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} {%- endfor -%} """ # remove the leading and trailing s diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da5060d7..66897308 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,15 +8,17 @@ on: branches: [ "master" ] concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: PACKAGE: "clash" REGISTRY: "ghcr.io" IMAGE_NAME: "clash-rs" + RUST_LOG: "clash=TRACE" +# Arm builder https://github.blog/changelog/2024-09-03-github-actions-arm64-linux-and-windows-runners-are-now-generally-available/ jobs: compile: name: ${{ matrix.release-name || matrix.target || 'Unknown' }} @@ -31,15 +33,15 @@ jobs: # when not set, default will be used (except target, cross) # - os: ubuntu-latest # target: x86_64-unknown-linux-gnu - # release-name: x86_64-linux - # toolchain: stable - # cross: true + # release-name: $target + # toolchain: nightly + # cross: false # postfix: "" # extra-args: "" # components: "" - # rustflags: "" + # rustflags: "--cfg tokio_unstable" - # Linux x86 + # Linux x86 gnu - os: ubuntu-latest target: x86_64-unknown-linux-gnu cross: true @@ -48,26 +50,25 @@ jobs: target: i686-unknown-linux-gnu cross: true extra-args: "--all-features" - # Linux x86 static-crt + # Linux x86 musl - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - release-name: x86_64-unknown-linux-gnu-static-crt + target: x86_64-unknown-linux-musl cross: true extra-args: "--all-features" - rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" - # Linux x86_64-unknown-linux-musl + # Linux x86 gnu static-crt - os: ubuntu-latest - target: x86_64-unknown-linux-musl - release-name: x86_64-unknown-linux-musl + target: x86_64-unknown-linux-gnu + release-name: x86_64-unknown-linux-gnu-static-crt cross: true extra-args: "--all-features" + rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" - os: ubuntu-latest target: i686-unknown-linux-gnu release-name: i686-unknown-linux-gnu-static-crt cross: true extra-args: "--all-features" rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" - # Linux arm + # Linux arm gnu - os: ubuntu-latest target: aarch64-unknown-linux-gnu cross: true @@ -80,29 +81,33 @@ jobs: target: armv7-unknown-linux-gnueabihf cross: true extra-args: "--all-features" - # Linux arm static-crt + # Linux arm musl - os: ubuntu-latest - target: aarch64-unknown-linux-gnu - release-name: aarch64-unknown-linux-gnu-static-crt + target: aarch64-unknown-linux-musl cross: true extra-args: "--all-features" - rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" - os: ubuntu-latest - target: aarch64-unknown-linux-musl - release-name: aarch64-unknown-linux-musl + target: armv7-unknown-linux-musleabihf cross: true extra-args: "--all-features" + # Linux arm gnu static-crt - os: ubuntu-latest - target: armv7-unknown-linux-gnueabi - release-name: armv7-unknown-linux-gnueabi-static-crt + target: aarch64-unknown-linux-gnu + release-name: aarch64-unknown-linux-gnu-static-crt cross: true extra-args: "--all-features" rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" - os: ubuntu-latest - target: armv7-unknown-linux-musleabihf + target: armv7-unknown-linux-gnueabi + release-name: armv7-unknown-linux-gnueabi-static-crt cross: true extra-args: "--all-features" - rustflags: "--cfg tokio_unstable" + rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" + # Linux RISC-V gnu + # - os: ubuntu-latest + # target: riscv64gc-unknown-linux-gnu + # cross: true + # extra-args: "--all-features" # Windows - os: windows-latest target: x86_64-pc-windows-msvc @@ -114,6 +119,11 @@ jobs: cross: false postfix: ".exe" extra-args: "--all-features" + - os: windows-latest + target: aarch64-pc-windows-msvc + cross: false + postfix: ".exe" + extra-args: --features "shadowsocks tuic" # Windows static-crt - os: windows-latest target: x86_64-pc-windows-msvc @@ -128,6 +138,27 @@ jobs: cross: false postfix: ".exe" extra-args: "--all-features" + rustflags: >- + -Ctarget-feature=+crt-static + -Clink-args=/NODEFAULTLIB:libvcruntimed.lib + -Clink-args=/NODEFAULTLIB:vcruntime.lib + -Clink-args=/NODEFAULTLIB:vcruntimed.lib + -Clink-args=/NODEFAULTLIB:libcmtd.lib + -Clink-args=/NODEFAULTLIB:msvcrt.lib + -Clink-args=/NODEFAULTLIB:msvcrtd.lib + -Clink-args=/NODEFAULTLIB:libucrt.lib + -Clink-args=/NODEFAULTLIB:libucrtd.lib + + -Clink-args=/DEFAULTLIB:libcmt.lib + -Clink-args=/DEFAULTLIB:libvcruntime.lib + -Clink-args=/DEFAULTLIB:ucrt.lib + --cfg tokio_unstable + - os: windows-latest + target: aarch64-pc-windows-msvc + release-name: aarch64-pc-windows-msvc-static-crt + cross: false + postfix: ".exe" + extra-args: --features "shadowsocks tuic" rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" # MacOSX - os: macos-12 @@ -152,7 +183,6 @@ jobs: extra-args: "--all-features" rustflags: "-Ctarget-feature=+crt-static --cfg tokio_unstable" # Linux mips: tier-3, pity - # Linux risc-v: needs update rustls # Windows gnu: tokio dont work # Windows aarch: todo @@ -165,6 +195,11 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ key: ${{ matrix.release-name || matrix.target }}-${{ hashFiles('**/Cargo.toml') }} restore-keys: | ${{ matrix.release-name || matrix.target }} @@ -174,18 +209,27 @@ jobs: toolchain: ${{ matrix.toolchain || 'nightly' }} target: ${{ matrix.target }} components: ${{ matrix.components || 'rustfmt, clippy' }} + - name: Install Protoc uses: arduino/setup-protoc@v3 with: version: "23.x" repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set environment variables + run: | + echo "CLASH_GIT_REF=${GITHUB_REF}" >> $GITHUB_ENV + echo "CLASH_GIT_SHA=${GITHUB_SHA}" >> $GITHUB_ENV + - name: Cargo fmt uses: clechasseur/rs-cargo@v2 with: use-cross: ${{ matrix.cross }} command: fmt args: --all -- --check - + env: + CLASH_DOCKER_TEST: "true" + - name: Cargo clippy uses: clechasseur/rs-cargo@v2 with: @@ -193,9 +237,9 @@ jobs: command: clippy args: --all --target ${{ matrix.target }} ${{ matrix.extra-args }} -- -D warnings env: - RUSTFLAGS: ${{ matrix.rustflags || '--cfg tokio_unstable' }} + CLASH_DOCKER_TEST: "true" - - name: Cargo test + - name: Cargo test (docker test on linux) uses: clechasseur/rs-cargo@v2 if: startsWith(matrix.os, 'ubuntu') with: @@ -204,17 +248,17 @@ jobs: args: --all --target ${{ matrix.target }} ${{ matrix.extra-args }} env: CROSS_CONTAINER_OPTS: "--network host" + CLASH_DOCKER_TEST: "true" RUSTFLAGS: ${{ matrix.rustflags || '--cfg tokio_unstable' }} - - name: Cargo test (no docker test) + - name: Cargo test (no docker test on windows-non-arm and macos) uses: clechasseur/rs-cargo@v2 - if: ${{ !startsWith(matrix.os, 'ubuntu') }} + if: ${{ !startsWith(matrix.os, 'ubuntu') && matrix.target != 'aarch64-pc-windows-msvc' }} with: use-cross: ${{ matrix.cross }} command: test args: --all --target ${{ matrix.target }} ${{ matrix.extra-args }} env: - CLASH_RS_CI: "true" RUSTFLAGS: ${{ matrix.rustflags || '--cfg tokio_unstable' }} - name: Cargo build @@ -245,7 +289,6 @@ jobs: release: name: Release - if: github.event_name != 'pull_request' needs: [ compile ] runs-on: ubuntu-latest steps: @@ -266,27 +309,26 @@ jobs: with: name: binaries path: ./packages - - - name: Delete previous latest tag + + - name: Create SHA256Sums.txt and version.txt run: | - git tag -d latest || true - git push origin :refs/tags/latest || true - gh release list | grep Draft | awk '{print $1 " \t"}' | while read -r line; do gh release delete -y "$line"; done - env: - GH_TOKEN: ${{ github.token }} - - - name: Create new latest tag - if: startsWith(github.ref, 'refs/heads/master') + cd packages + sha256sum * >> sha256sums.txt + cat sha256sums.txt + chmod +x ./clash-x86_64-unknown-linux-musl + ./clash-x86_64-unknown-linux-musl -v >> version.txt + cat version.txt + + - name: Move latest tag for git-cliff nightly run: | - git tag latest - git push origin latest + git tag latest -f - name: Generate a changelog uses: orhun/git-cliff-action@main id: git-cliff with: config: .github/cliff.toml - args: --latest --strip header + args: --latest --strip header env: GITHUB_REPO: ${{ github.repository }} @@ -294,31 +336,51 @@ jobs: uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/v') with: - token: "${{ secrets.GITHUB_TOKEN }}" + token: ${{ secrets.GITHUB_TOKEN }} prerelease: false generate_release_notes: false - body: "${{ steps.git-cliff.outputs.content }}" + body: ${{ steps.git-cliff.outputs.content }} files: | packages/* LICENSE* - + + - name: Delete old latest nightly release + if: startsWith(github.ref, 'refs/heads/master') + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const tag = 'latest' + const { owner, repo } = context.repo + const { data: { id } } = await github.rest.repos.getReleaseByTag({ owner, repo, tag }) + await github.rest.repos.deleteRelease({ owner, repo, release_id: id }) + + - name: Update Latest Tag + if: startsWith(github.ref, 'refs/heads/master') + uses: richardsimko/update-tag@v1 + with: + tag_name: latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Github nightly release uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/heads/master') with: - token: "${{ secrets.GITHUB_TOKEN }}" + token: ${{ secrets.GITHUB_TOKEN }} prerelease: true tag_name: "latest" generate_release_notes: false - body: "${{ steps.git-cliff.outputs.content }}" + body: ${{ steps.git-cliff.outputs.content }} files: | packages/* LICENSE* + docker-image: needs: [ compile ] name: Docker Image + if: ${{ startsWith(github.ref, 'refs/tags/v') }} runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') permissions: contents: read packages: write @@ -327,10 +389,8 @@ jobs: with: submodules: true - - name: Get the current Git commit hash - id: get-info + - name: Set docker image's tag run: | - echo "OWNER=${GITHUB_REPOSITORY_OWNER@L}" >> $GITHUB_OUTPUT echo "TAG_VERSION=${REGISTRY}/${GITHUB_REPOSITORY_OWNER@L}/${IMAGE_NAME}:${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV echo "TAG_LATEST=${REGISTRY}/${GITHUB_REPOSITORY_OWNER@L}/${IMAGE_NAME}:latest" >> $GITHUB_ENV @@ -340,13 +400,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ steps.get-info.outputs.OWNER }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Download binaries amd64 uses: actions/download-artifact@v4 with: @@ -363,7 +416,14 @@ jobs: run: | mv ./clash-rs/clash-x86_64-unknown-linux-musl ./clash-rs/clash-amd64 mv ./clash-rs/clash-aarch64-unknown-linux-musl ./clash-rs/clash-arm64 - + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: watfaq + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push release uses: docker/build-push-action@v6 with: diff --git a/Cargo.lock b/Cargo.lock index 59a7bd75..e69de29b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7242 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "zeroize", -] - -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "amplify" -version = "4.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7147b742325842988dd6c793d55f58df3ae36bccf7d9b6e07db10ab035be343d" -dependencies = [ - "amplify_derive", - "amplify_num", - "ascii", - "wasm-bindgen", -] - -[[package]] -name = "amplify_derive" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" -dependencies = [ - "amplify_syn", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "amplify_num" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "amplify_syn" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "arti-client" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c6c58e0fe132049f6c79c256c8f181df9556cca7fd7e6a5a24f1665151624dd" -dependencies = [ - "async-trait", - "cfg-if", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "educe", - "fs-mistrust", - "futures", - "hostname-validator", - "humantime", - "humantime-serde", - "libc", - "postage", - "rand", - "safelog", - "serde", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-circmgr", - "tor-config", - "tor-dirmgr", - "tor-error", - "tor-guardmgr", - "tor-hsclient", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - -[[package]] -name = "async-compression" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" -dependencies = [ - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite", -] - -[[package]] -name = "async-recursion" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "async-trait" -version = "0.1.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "async_executors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" -dependencies = [ - "blanket", - "futures-core", - "futures-task", - "futures-util", - "pin-project", - "rustc_version", - "tokio", -] - -[[package]] -name = "asynchronous-codec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" -dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "base64 0.21.2", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper 1.0.1", - "tokio", - "tokio-tungstenite 0.21.0", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.7.1", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.10.5", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.77", - "which", -] - -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.10.5", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.77", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "blake3" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[package]] -name = "blanket" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bollard" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" -dependencies = [ - "base64 0.22.1", - "bollard-stubs", - "bytes", - "futures-core", - "futures-util", - "hex", - "http", - "http-body-util", - "hyper", - "hyper-named-pipe", - "hyper-util", - "hyperlocal", - "log", - "pin-project-lite", - "serde", - "serde_derive", - "serde_json", - "serde_repr", - "serde_urlencoded", - "thiserror", - "tokio", - "tokio-util", - "tower-service", - "url", - "winapi", -] - -[[package]] -name = "bollard-stubs" -version = "1.45.0-rc.26.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" -dependencies = [ - "serde", - "serde_repr", - "serde_with", -] - -[[package]] -name = "boringtun" -version = "0.6.0" -source = "git+https://github.com/cloudflare/boringtun.git?rev=f672bb6c1e1e371240a8d151f15854687eb740bb#f672bb6c1e1e371240a8d151f15854687eb740bb" -dependencies = [ - "aead", - "base64 0.13.1", - "blake2", - "chacha20poly1305", - "hex", - "hmac", - "ip_network", - "ip_network_table", - "libc", - "nix", - "parking_lot", - "rand_core", - "ring 0.17.8", - "tracing", - "untrusted 0.9.0", - "x25519-dalek", -] - -[[package]] -name = "bounded-vec-deque" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2225b558afc76c596898f5f1b3fc35cfce0eb1b13635cbd7d1b2a7177dc10ccd" - -[[package]] -name = "brotli" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "by_address" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" - -[[package]] -name = "byte_string" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11aade7a05aa8c3a351cedc44c3fc45806430543382fcc4743a9b757a2a0b4ed" - -[[package]] -name = "bytemuck" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" - -[[package]] -name = "c2rust-bitfields" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41" -dependencies = [ - "c2rust-bitfields-derive", -] - -[[package]] -name = "c2rust-bitfields-derive" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "camellia" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" -dependencies = [ - "byteorder", - "cipher", -] - -[[package]] -name = "caret" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c979a125c4d00f63d49b648530a952c6cc42e3387cc96f41f9a4687ee6b9273" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" -dependencies = [ - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfb-mode" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" -dependencies = [ - "cipher", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading 0.7.4", -] - -[[package]] -name = "clap" -version = "4.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.1", -] - -[[package]] -name = "clap_derive" -version = "4.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "clash-rs" -version = "0.3.2" -dependencies = [ - "clap", - "clash_lib", -] - -[[package]] -name = "clash_doc" -version = "0.3.2" -dependencies = [ - "clash_lib", -] - -[[package]] -name = "clash_lib" -version = "0.3.2" -dependencies = [ - "aead", - "aes", - "aes-gcm", - "anyhow", - "arti-client", - "async-recursion", - "async-trait", - "axum", - "axum-macros", - "base64 0.22.1", - "blake2", - "bollard", - "boringtun", - "brotli", - "byteorder", - "bytes", - "cfb-mode", - "chacha20poly1305", - "chrono", - "console-subscriber", - "const-fnv1a-hash", - "crc32fast", - "criterion", - "dhcproto", - "digest", - "erased-serde", - "filetime", - "foreign-types-shared", - "futures", - "h2", - "h3", - "h3-quinn", - "hickory-client", - "hickory-proto 0.25.0-alpha.2", - "hickory-resolver 0.25.0-alpha.2", - "hickory-server", - "hmac", - "http", - "http-body-util", - "httparse", - "hyper", - "hyper-rustls", - "hyper-util", - "ip_network_table-deps-treebitmap", - "ipnet", - "libc", - "lru_time_cache", - "maxminddb", - "md-5", - "memory-stats", - "mockall", - "murmur3", - "netstack-lwip", - "network-interface", - "once_cell", - "opentelemetry", - "opentelemetry-jaeger-propagator", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", - "prost", - "prost-build", - "public-suffix", - "quinn", - "quinn-proto", - "rand", - "regex", - "register-count", - "ring-compat", - "rustls", - "rustls-pemfile", - "security-framework", - "serde", - "serde_json", - "serde_yaml", - "serial_test", - "sha1", - "sha2", - "shadowsocks", - "smoltcp", - "socket2", - "tempfile", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-test", - "tokio-tungstenite 0.24.0", - "tokio-util", - "tor-rtcompat", - "tower 0.5.1", - "tower-http", - "tracing", - "tracing-appender", - "tracing-opentelemetry", - "tracing-oslog", - "tracing-subscriber", - "tracing-timing", - "tuic", - "tuic-quinn", - "tun", - "url", - "uuid", - "webpki-roots", - "windows 0.58.0", -] - -[[package]] -name = "coarsetime" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" -dependencies = [ - "libc", - "wasix", - "wasm-bindgen", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console-api" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ed14aa9c9f927213c6e4f3ef75faaad3406134efe84ba2cb7983431d5f0931" -dependencies = [ - "futures-core", - "prost", - "prost-types", - "tonic", - "tracing-core", -] - -[[package]] -name = "console-subscriber" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e3a111a37f3333946ebf9da370ba5c5577b18eb342ec683eb488dd21980302" -dependencies = [ - "console-api", - "crossbeam-channel", - "crossbeam-utils", - "futures-task", - "hdrhistogram", - "humantime", - "hyper-util", - "prost", - "prost-types", - "serde", - "serde_json", - "thread_local", - "tokio", - "tokio-stream", - "tonic", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "const-fnv1a-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.11.1", - "syn 2.0.77", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core 0.20.10", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "defmt" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" -dependencies = [ - "defmt-parser", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "defmt-parser" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" -dependencies = [ - "thiserror", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derive-adhoc" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5283ac2881753c76c0892406705553f0d9ab30649f81e18964d3408f4501edb8" -dependencies = [ - "derive-adhoc-macros", - "heck 0.4.1", -] - -[[package]] -name = "derive-adhoc-macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c21b673a9b8c78c34908e6fcb42b922e11c4df2de5237f1c3f58d3285904a84b" -dependencies = [ - "heck 0.4.1", - "itertools 0.10.5", - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "sha3", - "strum 0.25.0", - "syn 1.0.109", - "void", -] - -[[package]] -name = "derive-deftly" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f9bc3564f74be6c35d49a7efee54380d7946ccc631323067f33fabb9246027" -dependencies = [ - "derive-deftly-macros", - "heck 0.4.1", -] - -[[package]] -name = "derive-deftly-macros" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b84d32b18d9a256d81e4fec2e4cfd0ab6dde5e5ff49be1713ae0adbd0060c2" -dependencies = [ - "heck 0.4.1", - "indexmap 1.9.3", - "itertools 0.10.5", - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "sha3", - "strum 0.26.3", - "syn 2.0.77", - "void", -] - -[[package]] -name = "derive_builder_core_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" -dependencies = [ - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" -dependencies = [ - "derive_builder_macro_fork_arti", -] - -[[package]] -name = "derive_builder_macro_fork_arti" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" -dependencies = [ - "derive_builder_core_fork_arti", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.77", -] - -[[package]] -name = "dhcproto" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6794294f2c4665aae452e950c2803a1e487c5672dc8448f0bfa3f52ff67e270" -dependencies = [ - "dhcproto-macros", - "hex", - "ipnet", - "rand", - "thiserror", - "trust-dns-proto", - "url", -] - -[[package]] -name = "dhcproto-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7993efb860416547839c115490d4951c6d0f8ec04a3594d9dd99d50ed7ec170" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "merlin", - "rand_core", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "educe" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "enum-as-inner" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "enum-ordinalize" -version = "3.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.0", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" -dependencies = [ - "crc32fast", - "miniz_oxide 0.8.0", -] - -[[package]] -name = "fluid-let" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "fs-mistrust" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43de34e45ddb3fc314aeae5c5b078b8e3549980cd45836f8364d7cca8d85aead" -dependencies = [ - "derive_builder_fork_arti", - "dirs", - "libc", - "once_cell", - "pwd-grp", - "serde", - "thiserror", - "walkdir", -] - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "futures-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" -dependencies = [ - "futures-io", - "rustls", - "rustls-pki-types", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[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", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "glob-match" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.5.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h3" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e7675a0963b47a6d12fe44c279918b4ffb19baee838ac37f48d2722ad5bc6ab" -dependencies = [ - "bytes", - "fastrand", - "futures-util", - "http", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "h3-quinn" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c799f413fceeea505236c4d8132f084ff4b55a652288d91439ee93dc24d855" -dependencies = [ - "bytes", - "futures", - "h3", - "quinn", - "tokio", - "tokio-util", -] - -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.2", - "byteorder", - "crossbeam-channel", - "flate2", - "nom", - "num-traits", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hickory-client" -version = "0.25.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90078f18923f3a7663f62fbd43c4030de758d16efae0071cdf3ef9c6358cbf49" -dependencies = [ - "cfg-if", - "data-encoding", - "futures-channel", - "futures-util", - "hickory-proto 0.25.0-alpha.2", - "once_cell", - "radix_trie", - "rand", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "hickory-proto" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner 0.6.1", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-proto" -version = "0.25.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8270a1857fb962b9914aafd46a89a187a4e63d0eb4190c327e7c7b8256a2d055" -dependencies = [ - "async-recursion", - "async-trait", - "bitflags 2.6.0", - "bytes", - "cfg-if", - "data-encoding", - "enum-as-inner 0.6.1", - "futures-channel", - "futures-io", - "futures-util", - "h2", - "http", - "idna 0.5.0", - "ipnet", - "once_cell", - "rand", - "ring 0.17.8", - "rustls", - "rustls-pemfile", - "serde", - "thiserror", - "time", - "tinyvec", - "tokio", - "tokio-rustls", - "tracing", - "url", -] - -[[package]] -name = "hickory-recursor" -version = "0.25.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d2b7bc3b967b53b1b9879e319c8cc4ec037bb5d25bc7a88edd2e6de15d1a70" -dependencies = [ - "async-recursion", - "async-trait", - "bytes", - "cfg-if", - "enum-as-inner 0.6.1", - "futures-util", - "hickory-proto 0.25.0-alpha.2", - "hickory-resolver 0.25.0-alpha.2", - "lru-cache", - "parking_lot", - "serde", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "hickory-resolver" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto 0.24.1", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "hickory-resolver" -version = "0.25.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c110355b5703070d9e29c344d79818a7cde3de9c27fc35750defea6074b0ad" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto 0.25.0-alpha.2", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "rustls", - "serde", - "smallvec", - "thiserror", - "tokio", - "tokio-rustls", - "tracing", -] - -[[package]] -name = "hickory-server" -version = "0.25.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee9bc516413439e322999f9c3263361b0454969cd53f20d26297ed8aa1e77c1" -dependencies = [ - "async-trait", - "bytes", - "cfg-if", - "enum-as-inner 0.6.1", - "futures-util", - "h2", - "hickory-proto 0.25.0-alpha.2", - "hickory-recursor", - "hickory-resolver 0.25.0-alpha.2", - "http", - "ipnet", - "prefix-trie", - "rustls", - "serde", - "thiserror", - "time", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "hostname-validator" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-named-pipe" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" -dependencies = [ - "hex", - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", - "winapi", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower 0.4.13", - "tower-service", - "tracing", -] - -[[package]] -name = "hyperlocal" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" -dependencies = [ - "hex", - "http-body-util", - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core 0.52.0", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - -[[package]] -name = "ioctl-sys" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" - -[[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" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4099b7cfc5c5e2fe8c5edf3f6f7adf7a714c9cc697534f63a5a5da30397cb2c0" -dependencies = [ - "ip_network", - "ip_network_table-deps-treebitmap", -] - -[[package]] -name = "ip_network_table-deps-treebitmap" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" -dependencies = [ - "serde", -] - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "kqueue" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", - "redox_syscall 0.5.4", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "lru_time_cache" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd" - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" - -[[package]] -name = "maxminddb" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6087e5d8ea14861bb7c7f573afbc7be3798d3ef0fae87ec4fd9a4de9a127c3c" -dependencies = [ - "ipnetwork", - "log", - "memchr", - "serde", -] - -[[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - -[[package]] -name = "memory-stats" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c73f5c649995a115e1a0220b35e4df0a1294500477f97a91d0660fb5abeb574a" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core", - "zeroize", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "mockall" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" - -[[package]] -name = "murmur3" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252111cf132ba0929b6f8e030cac2a24b507f3a4d6db6fb2896f27b354c714b" - -[[package]] -name = "netstack-lwip" -version = "0.3.4" -source = "git+https://github.com/Watfaq/netstack-lwip.git?rev=2817bf82740e04bbee6b7bf1165f55657a6ed163#2817bf82740e04bbee6b7bf1165f55657a6ed163" -dependencies = [ - "anyhow", - "bindgen 0.69.4", - "bytes", - "cc", - "futures", - "log", - "thiserror", - "tokio", -] - -[[package]] -name = "network-interface" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433419f898328beca4f2c6c73a1b52540658d92b0a99f0269330457e0fd998d5" -dependencies = [ - "cc", - "libc", - "thiserror", - "winapi", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.6.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "walkdir", - "windows-sys 0.48.0", -] - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "oneshot-fused-workaround" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7728ac6298f91a8a364fc9b33b3cfb8bb58c4ef134193dad6e5240739ffe26" -dependencies = [ - "futures", -] - -[[package]] -name = "oorandom" -version = "11.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "opentelemetry" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803801d3d3b71cd026851a53f974ea03df3d179cb758b260136a6c9e22e196af" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", -] - -[[package]] -name = "opentelemetry-jaeger-propagator" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca4418db06b49e0c150b3cba4643d3848ccd71200345082e273adbd697864f2" -dependencies = [ - "opentelemetry", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "596b1719b3cab83addb20bcbffdf21575279d9436d9ccccfe651a3bf0ab5ab06" -dependencies = [ - "async-trait", - "futures-core", - "http", - "opentelemetry", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "thiserror", - "tokio", - "tonic", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c43620e8f93359eb7e627a3b16ee92d8585774986f24f2ab010817426c5ce61" -dependencies = [ - "opentelemetry", - "opentelemetry_sdk", - "prost", - "tonic", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8e442487022a943e2315740e443dc5ee95fd541c18f509a5a6251b408a9f95" - -[[package]] -name = "opentelemetry_sdk" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0da0d6b47a3dbc6e9c9e36a0520e25cf943e046843818faaa3f87365a548c82" -dependencies = [ - "async-trait", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "once_cell", - "opentelemetry", - "percent-encoding", - "rand", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core", - "sha2", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[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 = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.5.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic 0.5.3", - "crossbeam-queue", - "futures", - "parking_lot", - "pin-project", - "static_assertions", - "thiserror", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "predicates" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" -dependencies = [ - "anstyle", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "prefix-trie" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cb065e4407d69a5a5265221262cceeafff7f1aabc545d01ed955cce92ee78b" -dependencies = [ - "ipnet", - "num-traits", -] - -[[package]] -name = "prettyplease" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" -dependencies = [ - "proc-macro2", - "syn 2.0.77", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "priority-queue" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560bcab673ff7f6ca9e270c17bf3affd8a05e3bd9207f123b0d45076fd8197e8" -dependencies = [ - "autocfg", - "equivalent", - "indexmap 2.5.0", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit 0.22.21", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.77", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "prost-types" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" -dependencies = [ - "prost", -] - -[[package]] -name = "public-suffix" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d9b181eb5a605445d04200022ead096987ea51978485b7280b79c6a3945343" - -[[package]] -name = "pwd-grp" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6955c41fd7e4283bdf6ff3e7218b7e3f8ef24c4236b31d22be050f4cfd5e2a2c" -dependencies = [ - "derive-adhoc", - "libc", - "paste", - "thiserror", -] - -[[package]] -name = "quanta" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" -dependencies = [ - "crossbeam-utils", - "libc", - "mach", - "once_cell", - "raw-cpuid", - "wasi 0.10.2+wasi-snapshot-preview1", - "web-sys", - "winapi", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quinn" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" -dependencies = [ - "bytes", - "futures-io", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.0.0", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" -dependencies = [ - "bytes", - "rand", - "ring 0.17.8", - "rustc-hash 2.0.0", - "rustls", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "raw-cpuid" -version = "10.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.3.4", - "regex-syntax 0.7.4", -] - -[[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", -] - -[[package]] -name = "regex-automata" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.4", -] - -[[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.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "register-count" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d8b2af7d3e6675306d6757f10b4cf0b218a9fa6a0b44d668f2132684ae4893" - -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - -[[package]] -name = "retry-error" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb501c6079c6e2a1c9761b76ddb12ecb6818b8773748f5e0394b95f838e4a38" - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "ring-compat" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccce7bae150b815f0811db41b8312fcb74bffa4cab9cee5429ee00f356dd5bd4" -dependencies = [ - "aead", - "digest", - "ecdsa", - "ed25519", - "generic-array", - "p256", - "p384", - "pkcs8", - "rand_core", - "ring 0.17.8", - "signature", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rusqlite" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" -dependencies = [ - "bitflags 2.6.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "time", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.12" -source = "git+https://github.com/Watfaq/rustls.git?rev=f84c0f8020b252978e9b157179e9a99233cd33aa#f84c0f8020b252978e9b157179e9a99233cd33aa" -dependencies = [ - "log", - "once_cell", - "ring 0.17.8", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring 0.17.8", - "rustls-pki-types", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "safelog" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cabd7492c13678058e680f161cf94ba34d9d9e48419d1fbc6c21a32926c23764" -dependencies = [ - "derive_more", - "educe", - "either", - "fluid-let", - "thiserror", -] - -[[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 = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "scc" -version = "2.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66" -dependencies = [ - "sdd", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sdd" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "sendfd" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604b71b8fc267e13bb3023a2c901126c8f349393666a6d98ac1ae5729b701798" -dependencies = [ - "libc", - "tokio", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "serde_ignored" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" -dependencies = [ - "itoa", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "serde_spanned" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.5.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" -dependencies = [ - "darling 0.20.10", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "serde_yaml" -version = "0.9.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" -dependencies = [ - "indexmap 2.5.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "serial_test" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" -dependencies = [ - "futures", - "log", - "once_cell", - "parking_lot", - "scc", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "shadowsocks" -version = "1.20.3" -source = "git+https://github.com/Watfaq/shadowsocks-rust?rev=c6cb7fd906fe9f4126f724ae252f8a67cc1926b1#c6cb7fd906fe9f4126f724ae252f8a67cc1926b1" -dependencies = [ - "aes", - "arc-swap", - "async-trait", - "base64 0.22.1", - "blake3", - "byte_string", - "bytes", - "cfg-if", - "futures", - "hickory-resolver 0.24.1", - "libc", - "log", - "lru_time_cache", - "notify", - "once_cell", - "percent-encoding", - "pin-project", - "rand", - "sendfd", - "serde", - "serde_json", - "serde_urlencoded", - "shadowsocks-crypto", - "socket2", - "spin 0.9.8", - "thiserror", - "tokio", - "tokio-tfo", - "url", - "windows-sys 0.59.0", -] - -[[package]] -name = "shadowsocks-crypto" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e49ecfad8b27f3df28848af11f08aa10df0c6b74b45748131753913be23373" -dependencies = [ - "aes", - "aes-gcm", - "blake3", - "bytes", - "camellia", - "cfg-if", - "chacha20", - "chacha20poly1305", - "ctr", - "hkdf", - "md-5", - "rand", - "ring-compat", - "sha1", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "smoltcp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "cfg-if", - "defmt", - "heapless", - "log", - "managed", -] - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ssh-cipher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" -dependencies = [ - "cipher", - "ssh-encoding", -] - -[[package]] -name = "ssh-encoding" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" -dependencies = [ - "base64ct", - "pem-rfc7468", - "sha2", -] - -[[package]] -name = "ssh-key" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc" -dependencies = [ - "p256", - "p384", - "p521", - "rand_core", - "rsa", - "sec1", - "sha2", - "signature", - "ssh-cipher", - "ssh-encoding", - "subtle", - "zeroize", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.77", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.77", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[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.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "git+https://github.com/Watfaq/tokio-rustls.git?rev=6b9af8ac7bb5abc159d9a67e9ddbf84127559a4a#6b9af8ac7bb5abc159d9a67e9ddbf84127559a4a" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-test" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" -dependencies = [ - "async-stream", - "bytes", - "futures-core", - "tokio", - "tokio-stream", -] - -[[package]] -name = "tokio-tfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb4382c6371e29365853d2b71e915d5398df46312a2158097d8bb3f54d0f1b4" -dependencies = [ - "cfg-if", - "futures", - "libc", - "log", - "once_cell", - "pin-project", - "socket2", - "tokio", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.21.0", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.24.0", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.21", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.5.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" -dependencies = [ - "indexmap 2.5.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.18", -] - -[[package]] -name = "tonic" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost", - "socket2", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tor-async-utils" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c008067156c51d6485b621d92e46ed8db544a6ad59b984b25e3686b73f086ea" -dependencies = [ - "educe", - "futures", - "oneshot-fused-workaround", - "pin-project", - "postage", - "void", -] - -[[package]] -name = "tor-basic-utils" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79d747dd7d631495c45e074250fad13cd83f9c751bc25fc3be5c9ca9b820a63" -dependencies = [ - "derive_more", - "hex", - "itertools 0.13.0", - "libc", - "paste", - "rand", - "rand_chacha", - "slab", - "thiserror", -] - -[[package]] -name = "tor-bytes" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9716213e8c95f8db1ae09bf73c8a36770a557eedd7cace5cd02d38af641b06a4" -dependencies = [ - "bytes", - "digest", - "educe", - "getrandom", - "thiserror", - "tor-error", - "tor-llcrypto", - "zeroize", -] - -[[package]] -name = "tor-cell" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a0ef0674d08e4ec1e7a6a8e0129784379463c72406aca987e82fdea9f4f0fd" -dependencies = [ - "bitflags 2.6.0", - "bytes", - "caret", - "derive_more", - "educe", - "paste", - "rand", - "smallvec", - "thiserror", - "tor-basic-utils", - "tor-bytes", - "tor-cert", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-units", -] - -[[package]] -name = "tor-cert" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb3afa49a44e1610c03b6142337ba0c4de1a6d70aea59849878de8876099930" -dependencies = [ - "caret", - "derive_more", - "digest", - "thiserror", - "tor-bytes", - "tor-checkable", - "tor-llcrypto", -] - -[[package]] -name = "tor-chanmgr" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fe321a802b53627477ca6f07c4660390d1f62c116a1aeb7ab943666bbbb1e6" -dependencies = [ - "async-trait", - "derive_builder_fork_arti", - "derive_more", - "educe", - "futures", - "oneshot-fused-workaround", - "postage", - "rand", - "safelog", - "serde", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-config", - "tor-error", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-proto", - "tor-rtcompat", - "tor-socksproto", - "tor-units", - "tracing", - "void", -] - -[[package]] -name = "tor-checkable" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d875e93e91977a7c2a1d6ba662d7a3f7d47fcfbad9b93c3a97c2ceb9acf7d29f" -dependencies = [ - "humantime", - "signature", - "thiserror", - "tor-llcrypto", -] - -[[package]] -name = "tor-circmgr" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5bc3db0f5ce25b183fc6832b9dcdaf50a2a7cef75651150743a51785f6071" -dependencies = [ - "amplify", - "async-trait", - "bounded-vec-deque", - "cfg-if", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs", - "dyn-clone", - "educe", - "futures", - "humantime-serde", - "itertools 0.13.0", - "once_cell", - "oneshot-fused-workaround", - "pin-project", - "rand", - "retry-error", - "safelog", - "serde", - "static_assertions", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-config", - "tor-error", - "tor-guardmgr", - "tor-linkspec", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tracing", - "void", - "weak-table", -] - -[[package]] -name = "tor-config" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47196b7671f195fba0145822455957aa6ad5005e8ed5e3599314842972908232" -dependencies = [ - "amplify", - "derive-deftly", - "derive_builder_fork_arti", - "directories", - "educe", - "either", - "figment", - "fs-mistrust", - "futures", - "itertools 0.13.0", - "notify", - "once_cell", - "paste", - "postage", - "regex", - "serde", - "serde-value", - "serde_ignored", - "shellexpand", - "strum 0.26.3", - "thiserror", - "toml", - "tor-basic-utils", - "tor-error", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "tor-consdiff" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aac77a0ec434b8ffeb1d67618e4dd0aeb1abd723ed5a34542575482b3dec1fc" -dependencies = [ - "digest", - "hex", - "thiserror", - "tor-llcrypto", -] - -[[package]] -name = "tor-dirclient" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c13767a064e9f0d17f6aaa307218d04abd5b770f042d167df39d6dd96311960" -dependencies = [ - "async-compression", - "base64ct", - "derive_more", - "futures", - "hex", - "http", - "httparse", - "httpdate", - "itertools 0.13.0", - "memchr", - "thiserror", - "tor-circmgr", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-proto", - "tor-rtcompat", - "tracing", -] - -[[package]] -name = "tor-dirmgr" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10788702ecc5ef2dc02292e36182732703bd2d3b497168a30bd29a19647f7f3c" -dependencies = [ - "async-trait", - "base64ct", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "event-listener", - "fs-mistrust", - "fslock", - "futures", - "hex", - "humantime", - "humantime-serde", - "itertools 0.13.0", - "memmap2", - "once_cell", - "oneshot-fused-workaround", - "paste", - "postage", - "rand", - "rusqlite", - "safelog", - "scopeguard", - "serde", - "signature", - "strum 0.26.3", - "thiserror", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-consdiff", - "tor-dirclient", - "tor-error", - "tor-guardmgr", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-rtcompat", - "tracing", -] - -[[package]] -name = "tor-error" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b3edc77493f64b7876a234e6d259ab209ec8d57e57ee9ed789b5e6047e2265e" -dependencies = [ - "derive_more", - "futures", - "once_cell", - "paste", - "retry-error", - "static_assertions", - "strum 0.26.3", - "thiserror", - "tracing", - "void", -] - -[[package]] -name = "tor-guardmgr" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da216f2d9b279ba65c27f7d5153a01bc002afaa5a7dea3cbd634a4af692736e3" -dependencies = [ - "amplify", - "base64ct", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "dyn-clone", - "educe", - "futures", - "humantime", - "humantime-serde", - "itertools 0.13.0", - "num_enum", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand", - "safelog", - "serde", - "strum 0.26.3", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-error", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-relay-selection", - "tor-rtcompat", - "tor-units", - "tracing", -] - -[[package]] -name = "tor-hsclient" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec233600388692f5b0c86304e446c1c5928d5389a4c1e9a458b9b3c9d2b0f1" -dependencies = [ - "async-trait", - "derive-deftly", - "derive_more", - "educe", - "either", - "futures", - "itertools 0.13.0", - "oneshot-fused-workaround", - "postage", - "rand", - "retry-error", - "safelog", - "slotmap", - "strum 0.26.3", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-rtcompat", - "tracing", -] - -[[package]] -name = "tor-hscrypto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db824b336c082804882221188097f73bcd8f551da2f56144c7b560c971f44f9" -dependencies = [ - "data-encoding", - "derive_more", - "digest", - "itertools 0.13.0", - "paste", - "rand", - "safelog", - "signature", - "subtle", - "thiserror", - "tor-basic-utils", - "tor-bytes", - "tor-error", - "tor-llcrypto", - "tor-units", -] - -[[package]] -name = "tor-keymgr" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e3442c4b1b9373eca3e95e27cd7ac81f5c63e9a5d6a1d7f756f9af53200640" -dependencies = [ - "amplify", - "arrayvec", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs", - "dyn-clone", - "fs-mistrust", - "glob-match", - "humantime", - "inventory", - "itertools 0.13.0", - "rand", - "serde", - "ssh-key", - "thiserror", - "tor-basic-utils", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-llcrypto", - "tor-persist", - "walkdir", - "zeroize", -] - -[[package]] -name = "tor-linkspec" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79554ce76d519f909a5bba8beea6b2187c4ba131a717944258dce7fcec235a8f" -dependencies = [ - "base64ct", - "by_address", - "caret", - "derive-deftly", - "derive_builder_fork_arti", - "derive_more", - "hex", - "itertools 0.13.0", - "safelog", - "serde", - "serde_with", - "strum 0.26.3", - "thiserror", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-llcrypto", - "tor-protover", -] - -[[package]] -name = "tor-llcrypto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2fe75fd5713ca4012a4047fcbd3d529c1db9f5ce7c9ab6f4630b503eab55a9" -dependencies = [ - "aes", - "base64ct", - "ctr", - "curve25519-dalek", - "derive_more", - "digest", - "ed25519-dalek", - "educe", - "getrandom", - "hex", - "rand_core", - "rsa", - "safelog", - "serde", - "sha1", - "sha2", - "sha3", - "signature", - "simple_asn1", - "subtle", - "thiserror", - "visibility", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "tor-log-ratelim" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8a5d8547bcbdd92d40267b863ff3482846972b1cfdbec4841c668a6539b4c0" -dependencies = [ - "futures", - "humantime", - "once_cell", - "thiserror", - "tor-error", - "tor-rtcompat", - "tracing", - "weak-table", -] - -[[package]] -name = "tor-netdir" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f493e5c390efa9625d5f44d0f33743fede0ff47bc1e4fada640a44e13004c963" -dependencies = [ - "bitflags 2.6.0", - "derive_more", - "digest", - "futures", - "hex", - "humantime", - "itertools 0.13.0", - "num_enum", - "rand", - "serde", - "static_assertions", - "strum 0.26.3", - "thiserror", - "time", - "tor-basic-utils", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-protover", - "tor-units", - "tracing", - "typed-index-collections", -] - -[[package]] -name = "tor-netdoc" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdce7a98b0d30652ca59c1e7c3595b2bc064eb805be56bc9b67a306a60d6592" -dependencies = [ - "amplify", - "base64ct", - "bitflags 2.6.0", - "cipher", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "hex", - "humantime", - "itertools 0.13.0", - "once_cell", - "phf", - "rand", - "serde", - "serde_with", - "signature", - "smallvec", - "subtle", - "thiserror", - "time", - "tinystr", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-protover", - "tor-units", - "void", - "weak-table", - "zeroize", -] - -[[package]] -name = "tor-persist" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b7942bb43a51129ae4e84124e82e48f96e453a6fb8381c5c2b23899116d411" -dependencies = [ - "derive-deftly", - "derive_more", - "filetime", - "fs-mistrust", - "fslock", - "futures", - "itertools 0.13.0", - "oneshot-fused-workaround", - "paste", - "sanitize-filename", - "serde", - "serde_json", - "thiserror", - "tor-async-utils", - "tor-basic-utils", - "tor-error", - "tracing", - "void", -] - -[[package]] -name = "tor-proto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec37cab7389f53751a02a01a0324aaf09a854b7a8ac56d0ebd44593fadde0b0" -dependencies = [ - "asynchronous-codec", - "bitvec", - "bytes", - "cipher", - "coarsetime", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "futures", - "hkdf", - "hmac", - "oneshot-fused-workaround", - "pin-project", - "rand", - "rand_core", - "safelog", - "subtle", - "thiserror", - "tokio", - "tokio-util", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-rtcompat", - "tor-rtmock", - "tor-units", - "tracing", - "typenum", - "visibility", - "void", - "zeroize", -] - -[[package]] -name = "tor-protover" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cf099c5c91216c7d0a6b2d4c67bb18f0786ad8c8273063d6a45c51b49b40c2" -dependencies = [ - "caret", - "thiserror", -] - -[[package]] -name = "tor-relay-selection" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c8aa5505d8e938ac9e75b819d803396fe69fb483c991b4495fe4b28d374a89c" -dependencies = [ - "rand", - "serde", - "tor-basic-utils", - "tor-linkspec", - "tor-netdir", - "tor-netdoc", -] - -[[package]] -name = "tor-rtcompat" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff8a108d6a5e8ae0a97cd1fa41c00360d86bce5e5d7bd0ee1566bcb25b44e44" -dependencies = [ - "async-trait", - "async_executors", - "coarsetime", - "derive_more", - "educe", - "futures", - "futures-rustls", - "paste", - "pin-project", - "rustls-pki-types", - "thiserror", - "tokio", - "tokio-util", - "tor-error", - "tracing", - "x509-signature", -] - -[[package]] -name = "tor-rtmock" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71824b2341740bc2338e554cd4832b692afa44e0eb11519b19ebbcd0179f0799" -dependencies = [ - "amplify", - "async-trait", - "derive-deftly", - "derive_more", - "educe", - "futures", - "humantime", - "itertools 0.13.0", - "oneshot-fused-workaround", - "pin-project", - "priority-queue", - "slotmap", - "strum 0.26.3", - "thiserror", - "tor-error", - "tor-rtcompat", - "tracing", - "tracing-test", - "void", -] - -[[package]] -name = "tor-socksproto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ea008c29b34604d49f25540e4d72b3bdce0d1021aa82f85e790262280804f0" -dependencies = [ - "caret", - "subtle", - "thiserror", - "tor-bytes", - "tor-error", -] - -[[package]] -name = "tor-units" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c549e18390341623fb8ee988b2622d9b8fa11727d66717c9331156f84e54b09d" -dependencies = [ - "derive_more", - "thiserror", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.6.0", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" -dependencies = [ - "crossbeam-channel", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-opentelemetry" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eabc56d23707ad55ba2a0750fc24767125d5a0f51993ba41ad2c441cc7b8dea" -dependencies = [ - "js-sys", - "once_cell", - "opentelemetry", - "opentelemetry_sdk", - "smallvec", - "tracing", - "tracing-core", - "tracing-log 0.2.0", - "tracing-subscriber", - "web-time", -] - -[[package]] -name = "tracing-oslog" -version = "0.2.0" -source = "git+https://github.com/Absolucy/tracing-oslog.git?branch=main#b44d62871464e332249ae79e000dad9ebeb06434" -dependencies = [ - "bindgen 0.70.1", - "cc", - "cfg-if", - "once_cell", - "parking_lot", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.1.3", -] - -[[package]] -name = "tracing-test" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" -dependencies = [ - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" -dependencies = [ - "quote", - "syn 2.0.77", -] - -[[package]] -name = "tracing-timing" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe4966d7b6ae25201de6ff9fa822afb0c9e933809187d5b82ad846ec108771b" -dependencies = [ - "crossbeam", - "doc-comment", - "fxhash", - "hdrhistogram", - "indexmap 1.9.3", - "quanta", - "slab", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "trust-dns-proto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner 0.5.1", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.2.3", - "ipnet", - "lazy_static", - "rand", - "smallvec", - "thiserror", - "tinyvec", - "tracing", - "url", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tuic" -version = "1.2.0" -source = "git+https://github.com/Itsusinn/tuic.git?tag=v1.2.0#da9118b323fa5d365a72e20a3d95eb4e79227365" -dependencies = [ - "bytes", - "futures-util", - "parking_lot", - "register-count", - "thiserror", - "uuid", -] - -[[package]] -name = "tuic-quinn" -version = "1.2.0" -source = "git+https://github.com/Itsusinn/tuic.git?tag=v1.2.0#da9118b323fa5d365a72e20a3d95eb4e79227365" -dependencies = [ - "bytes", - "futures-util", - "quinn", - "thiserror", - "tuic", - "uuid", -] - -[[package]] -name = "tun" -version = "0.6.1" -source = "git+https://github.com/Watfaq/rust-tun.git?rev=8f7568190f1200d3e272ca534baf8d1578147e18#8f7568190f1200d3e272ca534baf8d1578147e18" -dependencies = [ - "byteorder", - "bytes", - "futures-core", - "ioctl-sys", - "libc", - "log", - "thiserror", - "tokio", - "tokio-util", - "wintun", -] - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "utf-8", -] - -[[package]] -name = "typed-index-collections" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183496e014253d15abbe6235677b1392dba2d40524c88938991226baa38ac7c4" - -[[package]] -name = "typeid" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "unsafe-libyaml" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" -dependencies = [ - "getrandom", - "rand", - "serde", - "uuid-macro-internal", -] - -[[package]] -name = "uuid-macro-internal" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e1ba1f333bd65ce3c9f27de592fcbc256dafe3af2717f56d7c87761fbaccf4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "visibility" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasix" -version = "0.12.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" -dependencies = [ - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.77", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "weak-table" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" - -[[package]] -name = "web-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "widestring" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" -dependencies = [ - "windows-core 0.51.1", - "windows-targets 0.48.5", -] - -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wintun" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29b83b0eca06dd125dbcd48a45327c708a6da8aada3d95a3f06db0ce4b17e0d4" -dependencies = [ - "c2rust-bitfields", - "libloading 0.8.5", - "log", - "thiserror", - "windows 0.51.1", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core", - "serde", - "zeroize", -] - -[[package]] -name = "x509-signature" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb2bc2a902d992cd5f471ee3ab0ffd6603047a4207384562755b9d6de977518" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] diff --git a/Cargo.toml b/Cargo.toml index 09430ac4..73e028a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ [workspace.package] -version = "0.3.2" +version = "0.6.0" repository = "https://github.com/Watfaq/clash-rs.git" edition = "2021" @@ -18,6 +18,7 @@ opt-level = "s" codegen-units = 1 lto = true strip = true +debug = 1 [patch.crates-io] tokio-rustls = { git = "https://github.com/Watfaq/tokio-rustls.git", rev = "6b9af8ac7bb5abc159d9a67e9ddbf84127559a4a"} diff --git a/Cross.toml b/Cross.toml index b46139fb..9e6aea46 100644 --- a/Cross.toml +++ b/Cross.toml @@ -5,8 +5,8 @@ pre-build = [ ] [build.env] -# Docker in docker -volumes = ["/var/run/docker.sock=/var/run/docker.sock"] +volumes = ["/var/run/docker.sock=/var/run/docker.sock"] # Docker in docker +passthrough = ["CLASH_GIT_REF", "CLASH_GIT_SHA", "RUSTFLAGS", "RUST_LOG", "CLASH_DOCKER_TEST"] [target.x86_64-unknown-linux-gnu] image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" diff --git a/README.md b/README.md index 6f4b176d..553d25d8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A custom protocol, rule based network proxy software. ## ✨ Features - 🌈 Flexible traffic routing rules based off source/destination IP/Domain/GeoIP etc. -- 📦 Local anti spoofing DNS with support of UDP/TCP/DoH/DoT remote. +- 📦 Local anti spoofing DNS with support of UDP/TCP/DoH/DoT remote, and expose it as a local UDP/TCP/DoH/DoT server. - 🛡 Run as an HTTP/Socks5 proxy, or utun device as a home network gateway. - ⚙️ Shadowsocks/Trojan/Vmess/Wireguard(userspace)/Tor/Tuic/Socks5(TCP/UDP) outbound support with different underlying trasports(gRPC/TLS/H2/WebSocket/etc.). - 🌍 Dynamic remote rule/proxy loader. diff --git a/clash/Cargo.toml b/clash/Cargo.toml index b318dccd..867c50d5 100644 --- a/clash/Cargo.toml +++ b/clash/Cargo.toml @@ -12,7 +12,11 @@ tracing = ["clash_lib/tracing"] bench = ["clash_lib/bench"] onion = ["clash_lib/onion"] +dhat-heap = ["dep:dhat"] + [dependencies] -clap = { version = "4.5.17", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } + +clash_lib = { path = "../clash_lib", version = "*", default-features = false } -clash_lib = { path = "../clash_lib", version = "*", default-features = false } \ No newline at end of file +dhat = { version = "0.3.3", optional = true } \ No newline at end of file diff --git a/clash/build.rs b/clash/build.rs new file mode 100644 index 00000000..c39a4d90 --- /dev/null +++ b/clash/build.rs @@ -0,0 +1,18 @@ +#![feature(let_chains)] +fn main() { + let vars = ["CLASH_GIT_REF", "CLASH_GIT_SHA"]; + for var in vars { + println!("cargo:rerun-if-env-changed={var}"); + } + + let version = if let Some("refs/heads/master") = option_env!("CLASH_GIT_REF") + && let Some(sha) = option_env!("CLASH_GIT_SHA") + { + let short_sha = &sha[..7]; + // Nightly relase below + format!("{}-alpha+sha.{short_sha}", env!("CARGO_PKG_VERSION")) + } else { + env!("CARGO_PKG_VERSION").into() + }; + println!("cargo:rustc-env=CLASH_VERSION_OVERRIDE={version}"); +} diff --git a/clash/src/main.rs b/clash/src/main.rs index c95f1f60..bb2be8dc 100644 --- a/clash/src/main.rs +++ b/clash/src/main.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + extern crate clash_lib as clash; use clap::Parser; @@ -8,7 +12,7 @@ use std::{ }; #[derive(Parser)] -#[clap(author, version, about, long_about = None)] +#[clap(author, about, long_about = None)] struct Cli { #[clap(short, long, value_parser, value_name = "DIRECTORY")] directory: Option, @@ -31,10 +35,34 @@ struct Cli { help = "Test configuration and exit" )] test_config: bool, + #[clap( + short, + long, + visible_short_aliases = ['V'], + value_parser, + default_value = "false", + help = "Print clash-rs version and exit" + )] + version: bool, + #[clap(short, long, help = "Additinally log to file")] + log_file: Option, } fn main() { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + let cli = Cli::parse(); + + if cli.version { + println!( + "{} {}", + env!("CARGO_PKG_NAME"), + env!("CLASH_VERSION_OVERRIDE") + ); + exit(0) + } + let file = cli .directory .as_ref() @@ -60,11 +88,12 @@ fn main() { } } } + match clash::start(clash::Options { config: clash::Config::File(file), cwd: cli.directory.map(|x| x.to_string_lossy().to_string()), rt: Some(TokioRuntime::MultiThread), - log_file: None, + log_file: cli.log_file, }) { Ok(_) => {} Err(_) => { diff --git a/clash/tests/data/config/Country-asn.mmdb b/clash/tests/data/config/Country-asn.mmdb new file mode 100644 index 00000000..f6ce9eed Binary files /dev/null and b/clash/tests/data/config/Country-asn.mmdb differ diff --git a/clash/tests/data/config/GeoLite2-ASN.mmdb b/clash/tests/data/config/GeoLite2-ASN.mmdb new file mode 100644 index 00000000..6a925726 Binary files /dev/null and b/clash/tests/data/config/GeoLite2-ASN.mmdb differ diff --git a/clash_lib/src/app/dns/test/test.cert b/clash/tests/data/config/dns.cert similarity index 100% rename from clash_lib/src/app/dns/test/test.cert rename to clash/tests/data/config/dns.cert diff --git a/clash_lib/src/app/dns/test/test.key b/clash/tests/data/config/dns.key similarity index 100% rename from clash_lib/src/app/dns/test/test.key rename to clash/tests/data/config/dns.key diff --git a/clash/tests/data/config/rules.yaml b/clash/tests/data/config/rules.yaml index 0819cf0d..29528100 100644 --- a/clash/tests/data/config/rules.yaml +++ b/clash/tests/data/config/rules.yaml @@ -2,12 +2,14 @@ port: 8888 socks-port: 8889 mixed-port: 8899 +tproxy-port: 8900 tun: - enable: true + enable: false device-id: "dev://utun1989" - route-all: true + route-all: false gateway: "198.19.0.1/32" + so-mark: 3389 # routes: # - 0.0.0.0/1 # - 128.0.0.0/1 @@ -17,12 +19,24 @@ ipv6: true dns: enable: true ipv6: true - listen: 127.0.0.1:53553 - # udp: 127.0.0.1:53553 - # tcp: 127.0.0.1:53553 - # dot: 127.0.0.1:53554 - # doh: 127.0.0.1:53555 - + listen: + udp: 127.0.0.1:53553 + tcp: 127.0.0.1:53553 + dot: + addr: 127.0.0.1:53554 + ca-cert: dns.crt + ca-key: dns.key + doh: + addr: 127.0.0.1:53555 + ca-cert: dns.crt + ca-key: dns.key + hostname: dns.example.com + doh3: + addr: 127.0.0.1:53555 + ca-cert: dns.crt + ca-key: dns.key + hostname: dns.example.com + # ipv6: false # when the false, response to AAAA questions will be empty # These nameservers are used to resolve the DNS nameserver hostnames below. @@ -52,13 +66,15 @@ dns: allow-lan: true mode: rule -log-level: debug +log-level: trace external-controller: :9090 external-ui: "public" # secret: "clash-rs" experimental: ignore-resolve-fail: true +asn-mmdb: Country-asn.mmdb + profile: store-selected: true store-fake-ip: false diff --git a/clash/tests/data/config/ss.yaml b/clash/tests/data/config/ss.yaml index 84abda0f..4c9aff0d 100644 --- a/clash/tests/data/config/ss.yaml +++ b/clash/tests/data/config/ss.yaml @@ -61,6 +61,12 @@ proxies: password: "password" udp: true +proxy-groups: + - name: "udp-relay" + type: relay + proxies: + - ss-01 + - ss-02 rules: - - MATCH, ss-01 + - MATCH, udp-relay ... diff --git a/clash/tests/data/config/tproxy.yaml b/clash/tests/data/config/tproxy.yaml new file mode 100644 index 00000000..4f6ce925 --- /dev/null +++ b/clash/tests/data/config/tproxy.yaml @@ -0,0 +1,9 @@ +--- +tproxy-port: 8900 + +mode: rule +log-level: debug + +rules: + - MATCH, DIRECT +... diff --git a/docker/docker-compose.yml b/clash/tests/data/docker/docker-compose.yml similarity index 100% rename from docker/docker-compose.yml rename to clash/tests/data/docker/docker-compose.yml diff --git a/docker/nginx/nginx.conf b/clash/tests/data/docker/nginx/nginx.conf similarity index 100% rename from docker/nginx/nginx.conf rename to clash/tests/data/docker/nginx/nginx.conf diff --git a/docker/ss/Dockerfile b/clash/tests/data/docker/ss/Dockerfile similarity index 100% rename from docker/ss/Dockerfile rename to clash/tests/data/docker/ss/Dockerfile diff --git a/docker/v2ray/cert.pem b/clash/tests/data/docker/v2ray/cert.pem similarity index 100% rename from docker/v2ray/cert.pem rename to clash/tests/data/docker/v2ray/cert.pem diff --git a/docker/v2ray/config.json b/clash/tests/data/docker/v2ray/config.json similarity index 100% rename from docker/v2ray/config.json rename to clash/tests/data/docker/v2ray/config.json diff --git a/docker/v2ray/key.pem b/clash/tests/data/docker/v2ray/key.pem similarity index 100% rename from docker/v2ray/key.pem rename to clash/tests/data/docker/v2ray/key.pem diff --git a/docker/wg/wg0.conf b/clash/tests/data/docker/wg/wg0.conf similarity index 100% rename from docker/wg/wg0.conf rename to clash/tests/data/docker/wg/wg0.conf diff --git a/clash_lib/Cargo.toml b/clash_lib/Cargo.toml index 16f382eb..e1d36582 100644 --- a/clash_lib/Cargo.toml +++ b/clash_lib/Cargo.toml @@ -10,7 +10,7 @@ shadowsocks = ["dep:shadowsocks"] tuic = ["dep:tuic", "dep:tuic-quinn", "dep:register-count"] tracing = [] bench = ["dep:criterion"] -onion = ["arti-client/onion-service-client"] +onion = ["dep:arti-client", "dep:tor-rtcompat", "arti-client/onion-service-client"] [dependencies] # Async @@ -39,12 +39,12 @@ webpki-roots = "0.26" # Error handing & logging thiserror = "1" anyhow = "1" -opentelemetry = "0.25" -opentelemetry_sdk = { version = "0.25", default-features = false, features = ["trace", "rt-tokio"] } -tracing-opentelemetry = "0.26" -opentelemetry-jaeger-propagator = "0.25" -opentelemetry-otlp = { version = "0.25" } -opentelemetry-semantic-conventions = "0.25" +opentelemetry = "0.26" +opentelemetry_sdk = { version = "0.26", default-features = false, features = ["trace", "rt-tokio"] } +tracing-opentelemetry = "0.27" +opentelemetry-jaeger-propagator = "0.26" +opentelemetry-otlp = { version = "0.26" } +opentelemetry-semantic-conventions = { version = "0.26", features = ["semconv_experimental"] } # Data structures url = "2" @@ -69,7 +69,7 @@ once_cell = "1" # Algorithms crc32fast = "1" -brotli = "6" +brotli = "7" hmac = "0.12" sha1 = "0.10" sha2 = "0.10" @@ -83,7 +83,7 @@ const-fnv1a-hash = "1" filetime = "0.2" axum = { version = "0.7", features = ["ws"] } -tower-http = { version = "0.5", features = ["fs", "trace", "cors"] } +tower-http = { version = "0.6", features = ["fs", "trace", "cors"] } chrono = { version = "0.4", features = ["serde"] } tun = { git = "https://github.com/Watfaq/rust-tun.git", rev = "8f7568190f1200d3e272ca534baf8d1578147e18", features = ["async"] } @@ -100,8 +100,8 @@ erased-serde = "0.4" # DNS hickory-client = "0.25.0-alpha.2" hickory-resolver = "0.25.0-alpha.2" -hickory-server = { version = "0.25.0-alpha.2", features = ["dns-over-rustls", "dns-over-https-rustls"] } -hickory-proto = { version = "0.25.0-alpha.2", features = ["dns-over-rustls", "dns-over-https-rustls"]} +hickory-server = { version = "0.25.0-alpha.2", features = ["dns-over-rustls", "dns-over-https-rustls", "dns-over-h3"] } +hickory-proto = { version = "0.25.0-alpha.2", features = ["dns-over-rustls", "dns-over-https-rustls", "dns-over-h3"]} dhcproto = "0.12" ring-compat = { version = "0.8", features = ["aead"] } @@ -112,18 +112,18 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-oslog = { branch = "main", git = "https://github.com/Absolucy/tracing-oslog.git" } tracing-appender = "0.2" -shadowsocks = { git = "https://github.com/Watfaq/shadowsocks-rust", rev = "c6cb7fd906fe9f4126f724ae252f8a67cc1926b1", optional = true, features=["aead-cipher-2022","stream-cipher"] } +shadowsocks = { version="1.21", optional = true, features=["aead-cipher-2022","stream-cipher"] } maxminddb = "0.24" public-suffix = "0.1" murmur3 = "0.5" -arti-client = { version = "0.22", default-features = false, features = ["tokio", "rustls", "static-sqlite"] } -tor-rtcompat = { version = "0.22", default-features = false } +arti-client = { version = "0.22", optional = true, default-features = false, features = ["tokio", "rustls", "static-sqlite"] } +tor-rtcompat = { version = "0.22", optional = true, default-features = false } # tuic -tuic = { tag = "v1.2.0", optional = true, git = "https://github.com/Itsusinn/tuic.git" } -tuic-quinn = { tag = "v1.2.0", optional = true, git = "https://github.com/Itsusinn/tuic.git" } -quinn = { version = "0.11", default-features = false, features = ["futures-io", "runtime-tokio", "rustls"] } +tuic = { tag = "v1.3.1", optional = true, git = "https://github.com/Itsusinn/tuic.git" } +tuic-quinn = { tag = "v1.3.1", optional = true, git = "https://github.com/Itsusinn/tuic.git" } +quinn = { version = "0.11", optional = true, default-features = false, features = ["futures-io", "runtime-tokio", "rustls"] } register-count = { version = "0.1", optional = true } console-subscriber = { version = "0.4" } @@ -138,18 +138,22 @@ blake2 = "0.10.6" digest = "0.10.7" [dev-dependencies] -tempfile = "3.12" +tempfile = "3.13" mockall = "0.13.0" tokio-test = "0.4.4" -axum-macros = "0.4.0" +axum-macros = "0.4.2" bollard = "0.17" -serial_test = "3.1.1" +serial_test = "3.1" +env_logger = "0.11" [build-dependencies] prost-build = "0.13" +[target.'cfg(target_os="linux")'.dependencies] +unix-udp-sock = { git = "https://github.com/Watfaq/unix-udp-sock.git", rev = "cd3e4eca43e6f3be82a2703c3d711b7e18fbfd18"} + [target.'cfg(macos)'.dependencies] -security-framework = "2.11.1" +security-framework = "3.0.0" [target.'cfg(windows)'.dependencies] windows = { version = "0.58", features = [ diff --git a/clash_lib/build.rs b/clash_lib/build.rs index 444b0e8f..b948ec80 100644 --- a/clash_lib/build.rs +++ b/clash_lib/build.rs @@ -1,8 +1,8 @@ fn main() -> std::io::Result<()> { - println!("cargo::rustc-check-cfg=cfg(ci)"); - println!("cargo:rerun-if-env-changed=CLASH_RS_CI"); - if std::env::var("CLASH_RS_CI").is_ok() { - println!("cargo::rustc-cfg=ci"); + println!("cargo::rustc-check-cfg=cfg(docker_test)"); + println!("cargo:rerun-if-env-changed=CLASH_DOCKER_TEST"); + if let Some("1" | "true") = option_env!("CLASH_DOCKER_TEST") { + println!("cargo::rustc-cfg=docker_test"); } println!("cargo:rerun-if-changed=src/common/geodata/geodata.proto"); diff --git a/clash_lib/src/app/api/handlers/config.rs b/clash_lib/src/app/api/handlers/config.rs index 6927c98c..7166061c 100644 --- a/clash_lib/src/app/api/handlers/config.rs +++ b/clash_lib/src/app/api/handlers/config.rs @@ -220,13 +220,11 @@ async fn patch_configs( inbound_manager.rebuild_listeners(ports); - if let Some(h) = global_state.inbound_listener_handle.take() { - h.abort() - } + global_state.inbound_listener_handle.abort(); let r = inbound_manager.get_runner().unwrap(); - global_state.inbound_listener_handle = Some(tokio::spawn(r)); + global_state.inbound_listener_handle = tokio::spawn(r); } if let Some(mode) = payload.mode { diff --git a/clash_lib/src/app/dispatcher/dispatcher_impl.rs b/clash_lib/src/app/dispatcher/dispatcher_impl.rs index 52cb517c..2d942f82 100644 --- a/clash_lib/src/app/dispatcher/dispatcher_impl.rs +++ b/clash_lib/src/app/dispatcher/dispatcher_impl.rs @@ -10,7 +10,7 @@ use crate::{ internal::proxy::{PROXY_DIRECT, PROXY_GLOBAL}, }, proxy::{datagram::UdpPacket, AnyInboundDatagram}, - session::Session, + session::{Session, SocksAddr}, }; use futures::{SinkExt, StreamExt}; use std::{ @@ -75,44 +75,55 @@ impl Dispatcher { } #[instrument(skip(self, sess, lhs))] - pub async fn dispatch_stream(&self, sess: Session, mut lhs: S) + pub async fn dispatch_stream(&self, mut sess: Session, mut lhs: S) where S: AsyncRead + AsyncWrite + Unpin + Send, { - let sess = if self.resolver.fake_ip_enabled() { - match sess.destination { - crate::session::SocksAddr::Ip(addr) => { - let ip = addr.ip(); + let dest: SocksAddr = match &sess.destination { + crate::session::SocksAddr::Ip(socket_addr) => { + if self.resolver.fake_ip_enabled() { + trace!("looking up fake ip: {}", socket_addr.ip()); + let ip = socket_addr.ip(); if self.resolver.is_fake_ip(ip).await { let host = self.resolver.reverse_lookup(ip).await; match host { - Some(host) => { - let mut sess = sess; - sess.destination = crate::session::SocksAddr::Domain( - host, - addr.port(), - ); - sess - } + Some(host) => (host, socket_addr.port()) + .try_into() + .expect("must be valid domain"), None => { error!("failed to reverse lookup fake ip: {}", ip); return; } } } else { - sess + (*socket_addr).into() + } + } else { + trace!("looking up resolve cache ip: {}", socket_addr.ip()); + if let Some(resolved) = + self.resolver.cached_for(socket_addr.ip()).await + { + (resolved, socket_addr.port()) + .try_into() + .expect("must be valid domain") + } else { + (*socket_addr).into() } } - crate::session::SocksAddr::Domain(..) => sess, } - } else { - sess + crate::session::SocksAddr::Domain(host, port) => { + (host.to_owned(), *port) + .try_into() + .expect("must be valid domain") + } }; + sess.destination = dest.clone(); + let mode = *self.mode.lock().unwrap(); let (outbound_name, rule) = match mode { RunMode::Global => (PROXY_GLOBAL, None), - RunMode::Rule => self.router.match_route(&sess).await, + RunMode::Rule => self.router.match_route(&mut sess).await, RunMode::Direct => (PROXY_DIRECT, None), }; @@ -253,28 +264,17 @@ impl Dispatcher { while let Some(packet) = local_r.next().await { let mut sess = sess.clone(); sess.source = packet.src_addr.clone().must_into_socket_addr(); - sess.destination = packet.dst_addr.clone(); - - // populate fake ip for route matching - let sess = if resolver.fake_ip_enabled() { - trace!("looking up fake ip for {sess}"); - match sess.destination { - crate::session::SocksAddr::Ip(addr) => { - let ip = addr.ip(); + + let dest: SocksAddr = match &packet.dst_addr { + crate::session::SocksAddr::Ip(socket_addr) => { + if resolver.fake_ip_enabled() { + let ip = socket_addr.ip(); if resolver.is_fake_ip(ip).await { - trace!("fake ip detected"); let host = resolver.reverse_lookup(ip).await; match host { - Some(host) => { - trace!("fake ip resolved to {}", host); - let mut sess = sess; - sess.destination = - crate::session::SocksAddr::Domain( - host, - addr.port(), - ); - sess - } + Some(host) => (host, socket_addr.port()) + .try_into() + .expect("must be valid domain"), None => { error!( "failed to reverse lookup fake ip: {}", @@ -284,24 +284,38 @@ impl Dispatcher { } } } else { - sess + (*socket_addr).into() } + } else if let Some(resolved) = + resolver.cached_for(socket_addr.ip()).await + { + (resolved, socket_addr.port()) + .try_into() + .expect("must be valid domain") + } else { + (*socket_addr).into() } - crate::session::SocksAddr::Domain(..) => sess, } - } else { - sess + crate::session::SocksAddr::Domain(host, port) => { + (host.to_owned(), *port) + .try_into() + .expect("must be valid domain") + } }; + sess.destination = dest.clone(); // mutate packet for fake ip let mut packet = packet; - packet.dst_addr = sess.destination.clone(); + // resolve is done in OutboundDatagramImpl so it's fine to have + // (Domain, port) here. ideally the OutboundDatagramImpl should only + // do Ip though? + packet.dst_addr = dest; let mode = *mode.lock().unwrap(); let (outbound_name, rule) = match mode { RunMode::Global => (PROXY_GLOBAL, None), - RunMode::Rule => router.match_route(&sess).await, + RunMode::Rule => router.match_route(&mut sess).await, RunMode::Direct => (PROXY_DIRECT, None), }; diff --git a/clash_lib/src/app/dns/config.rs b/clash_lib/src/app/dns/config.rs index 7e7a5382..46169593 100644 --- a/clash_lib/src/app/dns/config.rs +++ b/clash_lib/src/app/dns/config.rs @@ -7,19 +7,17 @@ use std::{ use ipnet::AddrParseError; use regex::Regex; -use rustls::pki_types::{CertificateDer, PrivateKeyDer}; + +use serde::Deserialize; use url::Url; use crate::{ - common::{trie, utils}, + common::trie, config::def::{DNSListen, DNSMode}, Error, }; -use super::{ - dns_client::DNSNetMode, - dummy_keys::{TEST_CERT, TEST_KEY}, -}; +use super::dns_client::DNSNetMode; #[derive(Clone, Debug)] pub struct NameServer { @@ -47,23 +45,42 @@ pub struct FallbackFilter { pub domain: Vec, } -#[derive(Debug)] +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] pub struct DoHConfig { - pub certificate_and_key: (Vec>, PrivateKeyDer<'static>), - pub dns_hostname: Option, + pub addr: SocketAddr, + pub ca_cert: DnsServerCert, + pub ca_key: DnsServerKey, + pub hostname: Option, } -#[derive(Debug)] +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] +pub struct DoH3Config { + pub addr: SocketAddr, + pub ca_cert: DnsServerCert, + pub ca_key: DnsServerKey, + pub hostname: Option, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] pub struct DoTConfig { - pub certificate_and_key: (Vec>, PrivateKeyDer<'static>), + pub addr: SocketAddr, + pub ca_cert: DnsServerCert, + pub ca_key: DnsServerKey, } -#[derive(Debug, Default)] +pub type DnsServerKey = Option; +pub type DnsServerCert = Option; + +#[derive(Debug, Default, Clone)] pub struct DNSListenAddr { pub udp: Option, pub tcp: Option, - pub doh: Option<(SocketAddr, DoHConfig)>, - pub dot: Option<(SocketAddr, DoTConfig)>, + pub doh: Option, + pub dot: Option, + pub doh3: Option, } #[derive(Default)] @@ -270,48 +287,82 @@ impl TryFrom<&crate::config::def::Config> for Config { }) } DNSListen::Multiple(map) => { - use std::path::Path; let mut udp = None; let mut tcp = None; let mut doh = None; + let mut doh3 = None; let mut dot = None; for (k, v) in map { - let addr = v.parse::().map_err(|_| { - Error::InvalidConfig(format!( - "invalid DNS listen address: {} -> {}", - k, v - )) - })?; match k.as_str() { - "udp" => udp = Some(addr), - "tcp" => tcp = Some(addr), + "udp" => { + let addr = v + .as_str() + .ok_or(Error::InvalidConfig(format!( + "invalid udp dns listen address - must \ + be string: {:?}", + v + )))? + .parse::() + .map_err(|_| { + Error::InvalidConfig(format!( + "invalid dns listen address: {:?}", + v + )) + })?; + udp = Some(addr) + } + "tcp" => { + let addr = v + .as_str() + .ok_or(Error::InvalidConfig(format!( + "invalid tcp dns listen address - must \ + be string: {:?}", + v + )))? + .parse::() + .map_err(|_| { + Error::InvalidConfig(format!( + "invalid dns listen address: {:?}", + v + )) + })?; + tcp = Some(addr) + } "doh" => { - let certs = - utils::load_cert_chain(Path::new(TEST_CERT)) - .unwrap(); - let priv_key = - utils::load_priv_key(Path::new(TEST_KEY)) - .unwrap(); - let c = DoHConfig { - certificate_and_key: (certs, priv_key), - dns_hostname: Some( - "dns.example.com".to_owned(), - ), - }; - doh = Some((addr, c)) + let c = + DoHConfig::deserialize(v).map_err(|x| { + Error::InvalidConfig(format!( + "invalid doh dns listen config: \ + {:?}", + x + )) + })?; + + doh = Some(c) } "dot" => { - let certs = - utils::load_cert_chain(Path::new(TEST_CERT)) - .unwrap(); - let priv_key = - utils::load_priv_key(Path::new(TEST_KEY)) - .unwrap(); - let c = DoTConfig { - certificate_and_key: (certs, priv_key), - }; - dot = Some((addr, c)) + let c = + DoTConfig::deserialize(v).map_err(|x| { + Error::InvalidConfig(format!( + "invalid dot dns listen config: \ + {:?}", + x + )) + })?; + dot = Some(c) + } + "doh3" => { + let c = + DoH3Config::deserialize(v).map_err(|x| { + Error::InvalidConfig(format!( + "invalid doh3 dns listen config: \ + {:?}", + x + )) + })?; + + doh3 = Some(c) } _ => { return Err(Error::InvalidConfig(format!( @@ -322,7 +373,13 @@ impl TryFrom<&crate::config::def::Config> for Config { } } - Ok(DNSListenAddr { udp, tcp, doh, dot }) + Ok(DNSListenAddr { + udp, + tcp, + doh, + dot, + doh3, + }) } }) .transpose()? diff --git a/clash_lib/src/app/dns/dns_client.rs b/clash_lib/src/app/dns/dns_client.rs index 3fbd300a..f4864307 100644 --- a/clash_lib/src/app/dns/dns_client.rs +++ b/clash_lib/src/app/dns/dns_client.rs @@ -372,7 +372,7 @@ async fn dns_stream_builder( let mut tls_config = ClientConfig::builder() .with_root_certificates(GLOBAL_ROOT_STORE.clone()) .with_no_client_auth(); - tls_config.alpn_protocols = vec!["dot".into()]; + tls_config.alpn_protocols = vec!["dot".into(), "h2".into()]; let fut = new_tcp_stream( *addr, diff --git a/clash_lib/src/app/dns/filters.rs b/clash_lib/src/app/dns/filters.rs index 97c8026f..0de03fa9 100644 --- a/clash_lib/src/app/dns/filters.rs +++ b/clash_lib/src/app/dns/filters.rs @@ -17,7 +17,7 @@ impl GeoIPFilter { impl FallbackIPFilter for GeoIPFilter { fn apply(&self, ip: &net::IpAddr) -> bool { self.1 - .lookup(*ip) + .lookup_contry(*ip) .map(|x| x.country) .is_ok_and(|x| x.is_some_and(|x| x.iso_code == Some(self.0.as_str()))) } diff --git a/clash_lib/src/app/dns/helper.rs b/clash_lib/src/app/dns/helper.rs index 098fc404..8b6db732 100644 --- a/clash_lib/src/app/dns/helper.rs +++ b/clash_lib/src/app/dns/helper.rs @@ -17,7 +17,7 @@ pub async fn make_clients( let mut rv = Vec::new(); for s in servers { - debug!("building nameserver: {:?}", s); + debug!("building nameserver: {}", s); let (host, port) = if s.net == DNSNetMode::Dhcp { (s.address.as_str(), "0") diff --git a/clash_lib/src/app/dns/mod.rs b/clash_lib/src/app/dns/mod.rs index 09b63aed..5b62a8e9 100644 --- a/clash_lib/src/app/dns/mod.rs +++ b/clash_lib/src/app/dns/mod.rs @@ -11,7 +11,6 @@ use mockall::automock; mod config; mod dhcp; mod dns_client; -mod dummy_keys; mod fakeip; mod filters; mod helper; @@ -63,6 +62,9 @@ pub trait ClashResolver: Sync + Send { enhanced: bool, ) -> anyhow::Result>; + async fn cached_for(&self, ip: std::net::IpAddr) -> Option; + + /// Used for DNS Server async fn exchange(&self, message: op::Message) -> anyhow::Result; /// Only used for look up fake IP diff --git a/clash_lib/src/app/dns/resolver/enhanced.rs b/clash_lib/src/app/dns/resolver/enhanced.rs index 218004ad..ea46952e 100644 --- a/clash_lib/src/app/dns/resolver/enhanced.rs +++ b/clash_lib/src/app/dns/resolver/enhanced.rs @@ -10,7 +10,7 @@ use std::{ time::Duration, }; use tokio::sync::RwLock; -use tracing::{debug, error, instrument, warn}; +use tracing::{debug, error, instrument, trace, warn}; use hickory_proto::{op, rr}; @@ -46,6 +46,9 @@ pub struct EnhancedResolver { policy: Option>>, fake_dns: Option, + + reverse_lookup_cache: + Option>>>, } impl EnhancedResolver { @@ -75,11 +78,13 @@ impl EnhancedResolver { policy: None, fake_dns: None, + + reverse_lookup_cache: None, } } pub async fn new( - cfg: &Config, + cfg: Config, store: ThreadSafeCacheFile, mmdb: Arc, ) -> Self { @@ -94,6 +99,8 @@ impl EnhancedResolver { policy: None, fake_dns: None, + + reverse_lookup_cache: None, }); Self { @@ -103,7 +110,7 @@ impl EnhancedResolver { Some(default_resolver.clone()), ) .await, - hosts: cfg.hosts.clone(), + hosts: cfg.hosts, fallback: if !cfg.fallback.is_empty() { Some( make_clients( @@ -199,6 +206,17 @@ impl EnhancedResolver { } _ => None, }, + + reverse_lookup_cache: Some(Arc::new(RwLock::new( + lru_time_cache::LruCache::with_expiry_duration_and_capacity( + Duration::from_secs(3), /* should be shorter than TTL so + * client won't be connecting to a + * different server after the ip is + * reverse mapped to hostname and + * being resolved again */ + 4096, + ), + ))), } } @@ -251,7 +269,7 @@ impl EnhancedResolver { m.add_query(q); m.set_recursion_desired(true); - match self.exchange(m).await { + match self.exchange(&m).await { Ok(result) => { let ip_list = EnhancedResolver::ip_list_of_message(&result); if !ip_list.is_empty() { @@ -264,14 +282,14 @@ impl EnhancedResolver { } } - async fn exchange(&self, message: op::Message) -> anyhow::Result { + async fn exchange(&self, message: &op::Message) -> anyhow::Result { if let Some(q) = message.query() { if let Some(lru) = &self.lru_cache { if let Some(cached) = lru.read().await.peek(q.to_string().as_str()) { return Ok(cached.clone()); } } - self.exchange_no_cache(&message).await + self.exchange_no_cache(message).await } else { Err(anyhow!("invalid query")) } @@ -436,6 +454,13 @@ impl EnhancedResolver { }) .collect() } + + async fn save_reverse_lookup(&self, ip: net::IpAddr, domain: String) { + if let Some(lru) = &self.reverse_lookup_cache { + trace!("reverse lookup cache insert: {} -> {}", ip, domain); + lru.write().await.insert(ip, domain); + } + } } #[async_trait] @@ -545,8 +570,33 @@ impl ClashResolver for EnhancedResolver { } } + async fn cached_for(&self, ip: net::IpAddr) -> Option { + if let Some(lru) = &self.reverse_lookup_cache { + if let Some(cached) = lru.read().await.peek(&ip) { + trace!("reverse lookup cache hit: {} -> {}", ip, cached); + return Some(cached.clone()); + } + } + + None + } + async fn exchange(&self, message: op::Message) -> anyhow::Result { - self.exchange(message).await + let rv = self.exchange(&message).await?; + let hostname = message + .query() + .unwrap() + .name() + .to_utf8() + .trim_end_matches('.') + .to_owned(); + let ip_list = EnhancedResolver::ip_list_of_message(&rv); + if !ip_list.is_empty() { + for ip in ip_list { + self.save_reverse_lookup(ip, hostname.clone()).await; + } + } + Ok(rv) } fn ipv6(&self) -> bool { diff --git a/clash_lib/src/app/dns/resolver/mod.rs b/clash_lib/src/app/dns/resolver/mod.rs index c1378b2a..d5aabf26 100644 --- a/clash_lib/src/app/dns/resolver/mod.rs +++ b/clash_lib/src/app/dns/resolver/mod.rs @@ -17,7 +17,7 @@ use crate::{app::profile::ThreadSafeCacheFile, common::mmdb::Mmdb}; use super::{Config, ThreadSafeDNSResolver}; pub async fn new( - cfg: &Config, + cfg: Config, store: Option, mmdb: Option>, ) -> ThreadSafeDNSResolver { diff --git a/clash_lib/src/app/dns/resolver/system_linux.rs b/clash_lib/src/app/dns/resolver/system_linux.rs index c16ce87f..44567ccd 100644 --- a/clash_lib/src/app/dns/resolver/system_linux.rs +++ b/clash_lib/src/app/dns/resolver/system_linux.rs @@ -56,6 +56,10 @@ impl ClashResolver for SystemResolver { Ok(response.iter().map(|x| x.0).choose(&mut rand::thread_rng())) } + async fn cached_for(&self, _: std::net::IpAddr) -> Option { + None + } + async fn exchange( &self, _: hickory_proto::op::Message, diff --git a/clash_lib/src/app/dns/resolver/system_non_linux.rs b/clash_lib/src/app/dns/resolver/system_non_linux.rs index 081b08cf..c306860a 100644 --- a/clash_lib/src/app/dns/resolver/system_non_linux.rs +++ b/clash_lib/src/app/dns/resolver/system_non_linux.rs @@ -75,6 +75,10 @@ impl ClashResolver for SystemResolver { Ok(response.into_iter().choose(&mut rand::thread_rng())) } + async fn cached_for(&self, _: std::net::IpAddr) -> Option { + None + } + async fn exchange( &self, _: hickory_proto::op::Message, diff --git a/clash_lib/src/app/dns/dummy_keys.rs b/clash_lib/src/app/dns/server/dummy_keys.rs similarity index 55% rename from clash_lib/src/app/dns/dummy_keys.rs rename to clash_lib/src/app/dns/server/dummy_keys.rs index ee755e29..80fdf85c 100644 --- a/clash_lib/src/app/dns/dummy_keys.rs +++ b/clash_lib/src/app/dns/server/dummy_keys.rs @@ -1,6 +1,5 @@ -/// Test certificate and key -/// host: dns.example.com -/// TODO(#51): use real certificate and key +//! Test certificate and key +//! host: dns.example.com pub static TEST_CERT: &str = include_str!("test/test.cert"); diff --git a/clash_lib/src/app/dns/server/mod.rs b/clash_lib/src/app/dns/server/mod.rs index 3075b4cc..248a3ada 100644 --- a/clash_lib/src/app/dns/server/mod.rs +++ b/clash_lib/src/app/dns/server/mod.rs @@ -1,3 +1,6 @@ +mod dummy_keys; +mod utils; + use std::{net::IpAddr, time::Duration}; use async_trait::async_trait; @@ -16,11 +19,12 @@ use hickory_server::{ }; use thiserror::Error; use tokio::net::{TcpListener, UdpSocket}; -use tracing::{debug, info, warn}; +use tracing::{debug, error, info, warn}; +use utils::{load_default_cert, load_default_key}; use crate::Runner; -use super::{Config, ThreadSafeDNSResolver}; +use super::{config::DNSListenAddr, ThreadSafeDNSResolver}; static DEFAULT_DNS_SERVER_TTL: u32 = 60; @@ -201,60 +205,120 @@ impl RequestHandler for DnsHandler { static DEFAULT_DNS_SERVER_TIMEOUT: Duration = Duration::from_secs(5); pub async fn get_dns_listener( - cfg: Config, + listen: DNSListenAddr, resolver: ThreadSafeDNSResolver, + cwd: &std::path::Path, ) -> Option { let h = DnsHandler { resolver }; let mut s = ServerFuture::new(h); let mut has_server = false; - if let Some(addr) = cfg.listen.udp { + if let Some(addr) = listen.udp { has_server = true; UdpSocket::bind(addr) .await .map(|x| { - info!("dns server listening on udp: {}", addr); + info!("UDP dns server listening on: {}", addr); s.register_socket(x); }) .ok()?; } - if let Some(addr) = cfg.listen.tcp { + if let Some(addr) = listen.tcp { has_server = true; TcpListener::bind(addr) .await .map(|x| { - info!("dns server listening on tcp: {}", addr); + info!("TCP dns server listening on: {}", addr); s.register_listener(x, DEFAULT_DNS_SERVER_TIMEOUT); }) .ok()?; } - if let Some(c) = cfg.listen.doh { + if let Some(c) = listen.doh { has_server = true; - TcpListener::bind(c.0) + TcpListener::bind(c.addr) .await .and_then(|x| { - info!("dns server listening on doh: {}", c.0); + info!("DoH server listening on: {}", c.addr); + if let (Some(k), Some(c)) = (&c.ca_key, &c.ca_cert) { + debug!("using custom key and cert for doh: {}/{}", k, c); + } + + let server_key = c + .ca_key + .map(|x| utils::load_priv_key(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_key()); + let server_cert = c + .ca_cert + .map(|x| utils::load_cert_chain(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_cert()); s.register_https_listener( x, DEFAULT_DNS_SERVER_TIMEOUT, - c.1.certificate_and_key, - c.1.dns_hostname, + (server_cert, server_key), + c.hostname, )?; Ok(()) }) .ok()?; } - if let Some(c) = cfg.listen.dot { + if let Some(c) = listen.dot { has_server = true; - TcpListener::bind(c.0) + TcpListener::bind(c.addr) .await .and_then(|x| { - info!("dns server listening on dot: {}", c.0); + info!("DoT dns server listening on: {}", c.addr); + if let (Some(k), Some(c)) = (&c.ca_key, &c.ca_cert) { + debug!("using custom key and cert for dot: {}/{}", k, c); + } + + let server_key = c + .ca_key + .map(|x| utils::load_priv_key(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_key()); + let server_cert = c + .ca_cert + .map(|x| utils::load_cert_chain(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_cert()); s.register_tls_listener( x, DEFAULT_DNS_SERVER_TIMEOUT, - c.1.certificate_and_key, + (server_cert, server_key), + )?; + Ok(()) + }) + .ok()?; + } + + if let Some(c) = listen.doh3 { + has_server = true; + UdpSocket::bind(c.addr) + .await + .and_then(|x| { + info!("DoT3 dns server listening on: {}", c.addr); + if let (Some(k), Some(c)) = (&c.ca_key, &c.ca_cert) { + debug!("using custom key and cert for dot: {}/{}", k, c); + } + + let server_key = c + .ca_key + .map(|x| utils::load_priv_key(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_key()); + let server_cert = c + .ca_cert + .map(|x| utils::load_cert_chain(&cwd.join(x))) + .transpose()? + .unwrap_or(load_default_cert()); + s.register_h3_listener( + x, + DEFAULT_DNS_SERVER_TIMEOUT, + (server_cert, server_key), + c.hostname, )?; Ok(()) }) @@ -274,3 +338,204 @@ pub async fn get_dns_listener( }) })) } + +#[cfg(test)] +mod tests { + use std::{sync::Arc, time::Duration}; + + use hickory_client::{ + client::{self, AsyncClient, ClientHandle}, + proto::iocompat::AsyncIoTokioAsStd, + }; + use hickory_proto::{ + h2::HttpsClientStreamBuilder, + h3::H3ClientStreamBuilder, + rr::{rdata::A, DNSClass, Name, RData, RecordType}, + rustls::tls_client_connect, + tcp::TcpClientStream, + udp::UdpClientStream, + }; + use rustls::ClientConfig; + use tokio::net::{TcpStream as TokioTcpStream, UdpSocket as TokioUdpSocket}; + + use crate::{ + app::dns::MockClashResolver, + common::tls::{self, GLOBAL_ROOT_STORE}, + tests::initialize, + }; + + async fn send_query(client: &mut AsyncClient) { + // Specify the name, note the final '.' which specifies it's an FQDN + let name = Name::from_ascii("www.example.com.").unwrap(); + + // NOTE: see 'Setup a connection' example above + // Send the query and get a message response, see RecordType for all + // supported options + let response = client + .query(name, DNSClass::IN, RecordType::A) + .await + .unwrap(); + + // Messages are the packets sent between client and server in DNS. + // there are many fields to a Message, DnsResponse can be dereferenced into + // a Message. It's beyond the scope of these examples + // to explain all the details of a Message. See + // hickory_client::op::message::Message for more details. generally + // we will be interested in the Message::answers + let answers = response.answers(); + + // Records are generic objects which can contain any data. + // In order to access it we need to first check what type of record it is + // In this case we are interested in A, IPv4 address + if let RData::A(ref ip) = answers[0].data() { + assert_eq!(*ip, A::new(93, 184, 215, 14)) + } else { + unreachable!("unexpected result") + } + } + + #[tokio::test] + async fn test_multiple_dns_server() { + initialize(); + + let mut resolver = MockClashResolver::new(); + resolver.expect_fake_ip_enabled().returning(|| false); + resolver.expect_exchange().returning(|_| { + let mut m = hickory_proto::op::Message::new(); + m.set_response_code(hickory_proto::op::ResponseCode::NoError); + m.add_answer(hickory_proto::rr::Record::from_rdata( + "www.example.com".parse().unwrap(), + 60, + hickory_proto::rr::RData::A(hickory_proto::rr::rdata::A( + std::net::Ipv4Addr::new(93, 184, 215, 14), + )), + )); + Ok(m) + }); + + let cfg = crate::app::dns::config::DNSListenAddr { + udp: Some("127.0.0.1:53553".parse().unwrap()), + tcp: Some("127.0.0.1:53554".parse().unwrap()), + dot: Some(crate::app::dns::config::DoTConfig { + addr: "127.0.0.1:53555".parse().unwrap(), + ca_key: None, + ca_cert: None, + }), + doh: Some(crate::app::dns::config::DoHConfig { + addr: "127.0.0.1:53556".parse().unwrap(), + hostname: Some("dns.example.com".to_string()), + ca_key: None, + ca_cert: None, + }), + doh3: Some(crate::app::dns::config::DoH3Config { + addr: "127.0.0.1:53556".parse().unwrap(), + hostname: Some("dns.example.com".to_string()), + ca_key: None, + ca_cert: None, + }), + }; + let listener = super::get_dns_listener( + cfg, + Arc::new(resolver), + std::path::Path::new("."), + ) + .await; + + assert!(listener.is_some()); + tokio::spawn(async move { + listener.unwrap().await.unwrap(); + }); + + let stream = UdpClientStream::::new( + "127.0.0.1:53553".parse().unwrap(), + ); + let (mut client, handle) = + client::AsyncClient::connect(stream).await.unwrap(); + tokio::spawn(handle); + + send_query(&mut client).await; + + let (stream, sender) = + TcpClientStream::>::new( + "127.0.0.1:53554".parse().unwrap(), + ); + + let (mut client, handle) = client::AsyncClient::new(stream, sender, None) + .await + .unwrap(); + tokio::spawn(handle); + + send_query(&mut client).await; + + let mut tls_config = ClientConfig::builder() + .with_root_certificates(GLOBAL_ROOT_STORE.clone()) + .with_no_client_auth(); + tls_config.alpn_protocols = vec!["h2".into()]; + tls_config + .dangerous() + .set_certificate_verifier(Arc::new(tls::DummyTlsVerifier::new())); + + let (stream, sender) = tls_client_connect::>( + "127.0.0.1:53555".parse().unwrap(), + "dns.example.com".to_owned(), + Arc::new(tls_config), + ); + + let (mut client, handle) = client::AsyncClient::with_timeout( + stream, + sender, + Duration::from_secs(5), + None, + ) + .await + .unwrap(); + tokio::spawn(handle); + + send_query(&mut client).await; + + let mut tls_config = ClientConfig::builder() + .with_root_certificates(GLOBAL_ROOT_STORE.clone()) + .with_no_client_auth(); + tls_config.alpn_protocols = vec!["h2".into()]; + + tls_config + .dangerous() + .set_certificate_verifier(Arc::new(tls::DummyTlsVerifier::new())); + + let stream = + HttpsClientStreamBuilder::with_client_config(Arc::new(tls_config)) + .build::>( + "127.0.0.1:53556".parse().unwrap(), + "dns.example.com".to_owned(), + ); + + let (mut client, handle) = + client::AsyncClient::connect(stream).await.unwrap(); + tokio::spawn(handle); + + send_query(&mut client).await; + + let mut tls_config = ClientConfig::builder() + .with_root_certificates(GLOBAL_ROOT_STORE.clone()) + .with_no_client_auth(); + tls_config.alpn_protocols = vec!["h3".into()]; + + tls_config + .dangerous() + .set_certificate_verifier(Arc::new(tls::DummyTlsVerifier::new())); + + let stream = H3ClientStreamBuilder::default() + .crypto_config(tls_config) + .clone() + .build( + "127.0.0.1:53556".parse().unwrap(), + "dns.example.com".to_owned(), + ); + + let (mut client, handle) = + client::AsyncClient::connect(stream).await.unwrap(); + tokio::spawn(handle); + + send_query(&mut client).await; + } +} diff --git a/clash_lib/src/app/dns/server/test/test.cert b/clash_lib/src/app/dns/server/test/test.cert new file mode 100644 index 00000000..4d9a8729 --- /dev/null +++ b/clash_lib/src/app/dns/server/test/test.cert @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDITCCAgmgAwIBAgIQOp++4lLpVGLWkaGuRrrP2zANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMB4XDTIzMDkwMjA4MzIyM1oXDTI0MDkwMTA4MzIy +M1owEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAPS+pfYexKHq50myjnY6kAZ1uLDmgR/Vy9y7rPcrkqci25ClCnuUiUgY +ABhrdq16oW2wOymc9A56YH9X99UNX6E+VmckCP8v9xcPV4/Zoc+KIeuJHRUBHFMK +ieiWe560XcRCTP7OudUZ+iSHbjhaxZJCEfFaE8aSZXmiLKUETZ7z2lAm75fRCBaR +Vy+jewy04F1BKg4VlnrZnVHzQCcBHZEpsAz4YpbNHMYf9sYRrh+T2j9Zya5tUklI +LzEobWXUzjhrkcBXSVC5dLa9sxz7eSeg0dY0SkP7O5pelr8LaXnBudAk5B0ByqFD +s83Z1AZMkv7vL2L8AGA/X1PP2UV3BrECAwEAAaNzMHEwDgYDVR0PAQH/BAQDAgKk +MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FIiHKw+Z9rUjV6pbqcDl+teSTLxoMBoGA1UdEQQTMBGCD2Rucy5leGFtcGxlLmNv +bTANBgkqhkiG9w0BAQsFAAOCAQEACTYIij1QqPhke3aI6HVLuXy75YHjxic0cIcX +7u41WSnZWOrnsBYAFPfjwH7sW6dX+5twUludSCSQ9Xyhl/tdQ0AKWJJNZ7irArS9 +kz2rU4u7YjtR4tuuRB2t+8UEcGA/m0EPhQfFbZedAy/Y2oc+RodwlqibVB/WCMOQ +BL4HS1wFaYZw9WhXk3nzb+wjBhvyEQkI9oeMqVYZLN/9kIY9QuGtDg5onFrVSgjZ +qiR7EfdICe8ogM6IiemQJfZ5SeWkoLpuRlaeVhqFFaFFeJ6cMTJ+Jluh6a3DGP91 +aRaPVO7r8gPq4mACua0HQcBfmH4VKS3hsQHdWDivRUT7xkZ/6g== +-----END CERTIFICATE----- diff --git a/clash_lib/src/app/dns/server/test/test.key b/clash_lib/src/app/dns/server/test/test.key new file mode 100644 index 00000000..eb2e6342 --- /dev/null +++ b/clash_lib/src/app/dns/server/test/test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0vqX2HsSh6udJ +so52OpAGdbiw5oEf1cvcu6z3K5KnItuQpQp7lIlIGAAYa3ateqFtsDspnPQOemB/ +V/fVDV+hPlZnJAj/L/cXD1eP2aHPiiHriR0VARxTConolnuetF3EQkz+zrnVGfok +h244WsWSQhHxWhPGkmV5oiylBE2e89pQJu+X0QgWkVcvo3sMtOBdQSoOFZZ62Z1R +80AnAR2RKbAM+GKWzRzGH/bGEa4fk9o/WcmubVJJSC8xKG1l1M44a5HAV0lQuXS2 +vbMc+3knoNHWNEpD+zuaXpa/C2l5wbnQJOQdAcqhQ7PN2dQGTJL+7y9i/ABgP19T +z9lFdwaxAgMBAAECggEAaMFNYcoLmc5kjsvJZFtumAU9NyKCNDEbX/BIeUcCL12h +IwkxMnICTIRRTiJ5Gom5nKxotkgCwkupD/iEEIH345k9/EmVPDy4gvtDHEQnmSBj +ol/+vaXLDNQe8Rmv8d77n2xNbmbnbYn/4jDBgYeAtzhmW6qVelHg8y3x8/OikZyy +EB+ORiH/p4XN+3xBuEnFwBjQSHlayYVL8E68rp2wcKIeB2wo27Noh+VNqc2jks/l +h9V+cydCOHY2OQ0iepZwPmsVVjEObtckksBTFWgpc5ZH6Ctq1YjtTpcOey9FRzhZ +fuK9cj7bO5uW1duocgMPGsgiYMdvGyOCmM4tgp4lwQKBgQD+SXkzutxu0577g3Td +8BWYvksM6O3Lq8ZHKQtcxcNpp9S/fQ134IMErlykS1FJ5290KkHwQ4tojiOhtYQo +GOlmbS7USR2puhNzEWxmhi0CIyyWyRDECrVZVnLfcVxZrxpoh4LjZFA6nVRhZipN +oMig1zDhVmhvUTrTxVygFp9/aQKBgQD2ZLgGTK0kZ4npsUEL/ZantlZwEpY8S3TK +muLlcGx3EFwPrX558xhYSwkh/afNTcRekJkoBA9SXaioZNKlOD/XyIFq0OeurygB +gC8RdBs3xAjNHyj9qxajhuBqxxxTa/HmUnWqH2zDv20DcyvLTXjpRuFBxBj77ka9 +eVyGBxmsCQKBgQCC2tJpIWagDXyJl2tDbnHeqUY7vX3pSlr9cYysUASwUTJ02+hb +YQhrF0MLNMr/Cf7bu4c1Gb0ar9J8O8lnTPKGx/bKPVnrZprtovCyjaeJqwoeChf7 +mjsaXxc8DrzkVex0EA/17kAu+ZlbidSJIA0+X56CxxF0/0sTgUOaCipHyQKBgQDl +Bie7y0fhH9CkhRtWPufrimP8FnrJHsY3kRK4e/CGF5HLDNQUHK8TWuPpUXLJNbEC +yVtjQ6rOP7qGk/jslEVbmMca94Vy7OK9yl111rt58WDQ8VbTu1T2uWceOWeN7zdR +hHJUqJMbvHJjE4mwlpl+FGFLFTC38/qTIhyrhCwLqQKBgQDz67mt3XyD5HUT/GYn +A91ZGjWfh3YPXYkjOOjw1MWqeO7LQi6A8uWkfRFPWn/h3gECqItrWFogJixRyL+G +TWQ7SWmJctHD8YjU3E6tlPSWQhD2U75pZWtjpGAIYSdvS5V9Q+95jaKKhsYneThB +SNEnosgWOR5eg9c0FznaDn6nRw== +-----END PRIVATE KEY----- diff --git a/clash_lib/src/app/dns/server/utils.rs b/clash_lib/src/app/dns/server/utils.rs new file mode 100644 index 00000000..93bbf0cd --- /dev/null +++ b/clash_lib/src/app/dns/server/utils.rs @@ -0,0 +1,39 @@ +use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; +use std::{fs, path::Path}; + +use crate::common::errors::new_io_error; + +use super::dummy_keys::{TEST_CERT, TEST_KEY}; + +pub fn load_cert_chain( + cert_path: &Path, +) -> std::io::Result>> { + let cert_chain = fs::read(cert_path)?; + if cert_path.extension().map_or(false, |x| x == "der") { + Ok(vec![CertificateDer::from(cert_chain)]) + } else { + rustls_pemfile::certs(&mut &*cert_chain).collect::>() + } +} + +pub fn load_priv_key(key_path: &Path) -> std::io::Result> { + let key = fs::read(key_path)?; + if key_path.extension().map_or(false, |x| x == "der") { + Ok(PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key))) + } else { + rustls_pemfile::private_key(&mut &*key)? + .ok_or(new_io_error("no private key found")) + } +} + +pub fn load_default_cert() -> Vec> { + rustls_pemfile::certs(&mut TEST_CERT.as_bytes()) + .collect::>() + .unwrap() +} + +pub fn load_default_key() -> PrivateKeyDer<'static> { + rustls_pemfile::private_key(&mut TEST_KEY.as_bytes()) + .unwrap() + .unwrap() +} diff --git a/clash_lib/src/app/inbound/manager.rs b/clash_lib/src/app/inbound/manager.rs index de5491bd..215712f3 100644 --- a/clash_lib/src/app/inbound/manager.rs +++ b/clash_lib/src/app/inbound/manager.rs @@ -6,7 +6,7 @@ use crate::{ dispatcher::Dispatcher, inbound::network_listener::{ListenerType, NetworkInboundListener}, }, - common::auth::ThreadSafeAuthenticator, + common::{auth::ThreadSafeAuthenticator, errors::new_io_error}, config::internal::config::{BindAddress, Inbound}, Error, Runner, }; @@ -68,7 +68,21 @@ impl InboundManager { } Ok(Box::pin(async move { - futures::future::select_all(runners).await.0 + let mut errors = Vec::new(); + let _ = futures::future::join_all(runners) + .await + .into_iter() + .filter_map(|r| r.map_err(|e| errors.push(e)).ok()) + .collect::>(); + if errors.is_empty() { + Ok(()) + } else { + Err(new_io_error(format!( + "failed to start inbound listeners: {:?}", + errors + )) + .into()) + } })) } @@ -101,6 +115,9 @@ impl InboundManager { ListenerType::Mixed => { ports.mixed_port = Some(x.port); } + ListenerType::Tproxy => { + ports.tproxy_port = Some(x.port); + } }); ports @@ -150,6 +167,20 @@ impl InboundManager { ); } + if let Some(tproxy_port) = ports.tproxy_port { + network_listeners.insert( + ListenerType::Tproxy, + NetworkInboundListener { + name: "TProxy".to_string(), + bind_addr: self.bind_address.clone(), + port: tproxy_port, + listener_type: ListenerType::Tproxy, + dispatcher: self.dispatcher.clone(), + authenticator: self.authenticator.clone(), + }, + ); + } + self.network_listeners = network_listeners; } } diff --git a/clash_lib/src/app/inbound/network_listener.rs b/clash_lib/src/app/inbound/network_listener.rs index 9ca2bea1..9b103264 100644 --- a/clash_lib/src/app/inbound/network_listener.rs +++ b/clash_lib/src/app/inbound/network_listener.rs @@ -4,6 +4,9 @@ use crate::{ use crate::proxy::{http, mixed, socks, AnyInboundListener}; +#[cfg(target_os = "linux")] +use crate::proxy::tproxy; + use crate::{proxy::utils::Interface, Dispatcher, Error, Runner}; use futures::FutureExt; use network_interface::{Addr, NetworkInterfaceConfig}; @@ -19,6 +22,7 @@ pub enum ListenerType { Http, Socks5, Mixed, + Tproxy, } pub struct NetworkInboundListener { @@ -107,21 +111,35 @@ impl NetworkInboundListener { fn build_and_insert_listener(&self, runners: &mut Vec, ip: Ipv4Addr) { let listener: AnyInboundListener = match self.listener_type { - ListenerType::Http => http::Listener::new( + ListenerType::Http => Arc::new(http::Listener::new( (ip, self.port).into(), self.dispatcher.clone(), self.authenticator.clone(), - ), - ListenerType::Socks5 => socks::Listener::new( + )), + ListenerType::Socks5 => Arc::new(socks::Listener::new( (ip, self.port).into(), self.dispatcher.clone(), self.authenticator.clone(), - ), - ListenerType::Mixed => mixed::Listener::new( + )), + ListenerType::Mixed => Arc::new(mixed::Listener::new( (ip, self.port).into(), self.dispatcher.clone(), self.authenticator.clone(), - ), + )), + ListenerType::Tproxy => { + #[cfg(target_os = "linux")] + { + Arc::new(tproxy::Listener::new( + (ip, self.port).into(), + self.dispatcher.clone(), + )) + } + #[cfg(not(target_os = "linux"))] + { + warn!("tproxy is not supported on this platform"); + return; + } + } }; if listener.handle_tcp() { diff --git a/clash_lib/src/app/logging.rs b/clash_lib/src/app/logging.rs index f7e73294..8a729129 100644 --- a/clash_lib/src/app/logging.rs +++ b/clash_lib/src/app/logging.rs @@ -15,7 +15,7 @@ use serde::Serialize; use tokio::sync::broadcast::Sender; use tracing::{debug, error}; -use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; +use tracing_appender::non_blocking::WorkerGuard; use tracing_oslog::OsLogger; use tracing_subscriber::{filter, filter::Directive, prelude::*, EnvFilter, Layer}; @@ -76,24 +76,6 @@ where } } -struct W(Option); - -impl std::io::Write for W { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - match self.0 { - Some(ref mut w) => w.write(buf), - None => Ok(buf.len()), - } - } - - fn flush(&mut self) -> std::io::Result<()> { - match self.0 { - Some(ref mut w) => w.flush(), - None => Ok(()), - } - } -} - pub fn setup_logging( level: LogLevel, collector: EventCollector, @@ -165,6 +147,15 @@ pub fn setup_logging( .with(filter) .with(collector) .with(console_layer) + .with(appender.map(|x| { + tracing_subscriber::fmt::Layer::new() + .with_ansi(false) + .compact() + .with_file(true) + .with_line_number(true) + .with_level(true) + .with_writer(x) + })) .with( tracing_subscriber::fmt::Layer::new() .with_ansi(std::io::stdout().is_terminal()) @@ -174,9 +165,6 @@ pub fn setup_logging( .with_line_number(true) .with_level(true) .with_thread_ids(true) - .with_writer(move || -> Box { - Box::new(W(appender.clone())) - }) .with_writer(std::io::stdout), ) .with(ios_os_log); @@ -193,7 +181,7 @@ pub fn setup_logging( struct EventVisitor<'a>(&'a mut Vec); -impl<'a> tracing::field::Visit for EventVisitor<'a> { +impl tracing::field::Visit for EventVisitor<'_> { fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { println!("bool {} = {}", field.name(), value); } diff --git a/clash_lib/src/app/outbound/manager.rs b/clash_lib/src/app/outbound/manager.rs index 527bcf35..5033d55d 100644 --- a/clash_lib/src/app/outbound/manager.rs +++ b/clash_lib/src/app/outbound/manager.rs @@ -25,7 +25,7 @@ use crate::{ OutboundProxyProviderDef, PROXY_DIRECT, PROXY_GLOBAL, PROXY_REJECT, }, proxy::{ - fallback, loadbalance, selector, socks, tor, trojan, + fallback, loadbalance, selector, socks, trojan, utils::{DirectConnector, ProxyConnector}, vmess, wg, OutboundType, }, @@ -44,6 +44,8 @@ use super::utils::proxy_groups_dag_sort; #[cfg(feature = "shadowsocks")] use crate::proxy::shadowsocks; +#[cfg(feature = "onion")] +use crate::proxy::tor; #[cfg(feature = "tuic")] use crate::proxy::tuic; @@ -278,6 +280,7 @@ impl OutboundManager { }); } + #[cfg(feature = "onion")] OutboundProxyProtocol::Tor(tor) => { handlers.insert(tor.name.clone(), { let h: tor::Handler = tor.try_into()?; @@ -393,8 +396,9 @@ impl OutboundManager { let relay = relay::Handler::new( relay::HandlerOptions { name: proto.name.clone(), - shared_opts: crate::proxy::HandlerSharedOptions { + common_opts: crate::proxy::HandlerCommonOptions { icon: proto.icon.clone(), + ..Default::default() }, }, providers, @@ -446,8 +450,9 @@ impl OutboundManager { let url_test = urltest::Handler::new( urltest::HandlerOptions { name: proto.name.clone(), - shared_opts: crate::proxy::HandlerSharedOptions { + common_opts: crate::proxy::HandlerCommonOptions { icon: proto.icon.clone(), + ..Default::default() }, ..Default::default() }, @@ -502,8 +507,9 @@ impl OutboundManager { let fallback = fallback::Handler::new( fallback::HandlerOptions { name: proto.name.clone(), - shared_opts: crate::proxy::HandlerSharedOptions { + common_opts: crate::proxy::HandlerCommonOptions { icon: proto.icon.clone(), + ..Default::default() }, ..Default::default() }, @@ -557,8 +563,9 @@ impl OutboundManager { let load_balance = loadbalance::Handler::new( loadbalance::HandlerOptions { name: proto.name.clone(), - shared_opts: crate::proxy::HandlerSharedOptions { + common_opts: crate::proxy::HandlerCommonOptions { icon: proto.icon.clone(), + ..Default::default() }, ..Default::default() }, @@ -616,8 +623,9 @@ impl OutboundManager { selector::HandlerOptions { name: proto.name.clone(), udp: proto.udp.unwrap_or(true), - shared_opts: crate::proxy::HandlerSharedOptions { + common_opts: crate::proxy::HandlerCommonOptions { icon: proto.icon.clone(), + ..Default::default() }, }, providers, @@ -654,7 +662,10 @@ impl OutboundManager { selector::HandlerOptions { name: PROXY_GLOBAL.to_owned(), udp: true, - shared_opts: crate::proxy::HandlerSharedOptions { icon: None }, + common_opts: crate::proxy::HandlerCommonOptions { + icon: None, + ..Default::default() + }, }, vec![pd.clone()], stored_selection, diff --git a/clash_lib/src/app/profile/mod.rs b/clash_lib/src/app/profile/mod.rs index 4dd2c7df..c458069b 100644 --- a/clash_lib/src/app/profile/mod.rs +++ b/clash_lib/src/app/profile/mod.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, sync::Arc}; use serde::{Deserialize, Serialize}; -use tracing::{error, trace}; +use tracing::{error, trace, warn}; #[derive(Serialize, Deserialize, Debug, Clone)] struct Db { @@ -119,7 +119,7 @@ impl CacheFile { } }, Err(e) => { - error!("failed to read cache file: {}, initializing a new one", e); + warn!("failed to read cache file: {}, initializing a new one", e); Db { selected: HashMap::new(), ip_to_host: HashMap::new(), diff --git a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs index 0abafd0d..21042472 100644 --- a/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs +++ b/clash_lib/src/app/remote_content_manager/providers/proxy_provider/proxy_set_provider.rs @@ -18,12 +18,14 @@ use crate::{ }, common::errors::map_io_error, config::internal::proxy::OutboundProxyProtocol, - proxy::{direct, reject, socks, tor, trojan, vmess, wg, AnyOutboundHandler}, + proxy::{direct, reject, socks, trojan, vmess, wg, AnyOutboundHandler}, Error, }; #[cfg(feature = "shadowsocks")] use crate::proxy::shadowsocks; +#[cfg(feature = "onion")] +use crate::proxy::tor; #[cfg(feature = "tuic")] use crate::proxy::tuic; @@ -140,6 +142,7 @@ impl ProxySetProvider { let h: wg::Handler = wg.try_into()?; Ok(Arc::new(h) as _) } + #[cfg(feature = "onion")] OutboundProxyProtocol::Tor(tor) => { let h: tor::Handler = tor.try_into()?; Ok(Arc::new(h) as _) diff --git a/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs b/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs index 2108b904..e26d934e 100644 --- a/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs +++ b/clash_lib/src/app/remote_content_manager/providers/rule_provider/provider.rs @@ -20,7 +20,9 @@ use crate::{ }, router::{map_rule_type, RuleMatcher}, }, - common::{errors::map_io_error, geodata::GeoData, mmdb::Mmdb, trie}, + common::{ + errors::map_io_error, geodata::GeoData, mmdb::Mmdb, succinct_set, trie, + }, config::internal::rule::RuleType, session::Session, Error, @@ -52,7 +54,8 @@ impl Display for RuleSetBehavior { } enum RuleContent { - Domain(trie::StringTrie), + // the left will converted into a right + Domain(succinct_set::DomainSet), Ipcidr(Box), Classical(Vec>), } @@ -91,7 +94,7 @@ impl RuleProviderImpl { let inner = Arc::new(tokio::sync::RwLock::new(Inner { content: match behovior { RuleSetBehavior::Domain => { - RuleContent::Domain(trie::StringTrie::new()) + RuleContent::Domain(succinct_set::DomainSet::default()) } RuleSetBehavior::Ipcidr => { RuleContent::Ipcidr(Box::new(CidrTrie::new())) @@ -150,9 +153,7 @@ impl RuleProvider for RuleProviderImpl { match inner { Ok(inner) => match &inner.content { - RuleContent::Domain(trie) => { - trie.search(&sess.destination.host()).is_some() - } + RuleContent::Domain(set) => set.has(&sess.destination.host()), RuleContent::Ipcidr(trie) => trie.contains( sess.destination .ip() @@ -243,7 +244,8 @@ fn make_rules( ) -> Result { match behavior { RuleSetBehavior::Domain => { - Ok(RuleContent::Domain(make_domain_rules(rules)?)) + let s = make_domain_rules(rules)?; + Ok(RuleContent::Domain(s.into())) } RuleSetBehavior::Ipcidr => { Ok(RuleContent::Ipcidr(Box::new(make_ip_cidr_rules(rules)?))) diff --git a/clash_lib/src/app/router/mod.rs b/clash_lib/src/app/router/mod.rs index aa40a211..29a51ace 100644 --- a/clash_lib/src/app/router/mod.rs +++ b/clash_lib/src/app/router/mod.rs @@ -9,14 +9,14 @@ use crate::{ use crate::{ common::mmdb::Mmdb, config::internal::{config::RuleProviderDef, rule::RuleType}, - session::{Session, SocksAddr}, + session::Session, }; use crate::app::router::rules::final_::Final; use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use hyper::Uri; -use tracing::{debug, error, info}; +use tracing::{error, info, trace}; use super::{ dns::ThreadSafeDNSResolver, @@ -33,9 +33,9 @@ pub use rules::RuleMatcher; pub struct Router { rules: Vec>, - #[allow(dead_code)] - rule_provider_registry: HashMap, dns_resolver: ThreadSafeDNSResolver, + + asn_mmdb: Option>, } pub type ThreadSafeRouter = Arc; @@ -47,7 +47,8 @@ impl Router { rules: Vec, rule_providers: HashMap, dns_resolver: ThreadSafeDNSResolver, - mmdb: Arc, + country_mmdb: Arc, + asn_mmdb: Option>, geodata: Arc, cwd: String, ) -> Self { @@ -57,7 +58,7 @@ impl Router { rule_providers, &mut rule_provider_registry, dns_resolver.clone(), - mmdb.clone(), + country_mmdb.clone(), geodata.clone(), cwd, ) @@ -70,52 +71,72 @@ impl Router { .map(|r| { map_rule_type( r, - mmdb.clone(), + country_mmdb.clone(), geodata.clone(), Some(&rule_provider_registry), ) }) .collect(), dns_resolver, - rule_provider_registry, + + asn_mmdb, } } + /// this mutates the session, attaching resolved IP and ASN pub async fn match_route( &self, - sess: &Session, + sess: &mut Session, ) -> (&str, Option<&Box>) { let mut sess_resolved = false; - let mut sess_dup = sess.clone(); for r in self.rules.iter() { if sess.destination.is_domain() && r.should_resolve_ip() && !sess_resolved { - debug!( - "rule `{r}` resolving domain {} locally", - sess.destination.domain().unwrap() - ); if let Ok(Some(ip)) = self .dns_resolver .resolve(sess.destination.domain().unwrap(), false) .await { - sess_dup.destination = - SocksAddr::from((ip, sess.destination.port())); + sess.resolved_ip = Some(ip); sess_resolved = true; } } - if r.apply(&sess_dup) { + let mayby_ip = sess.resolved_ip.or(sess.destination.ip()); + if let (Some(ip), Some(asn_mmdb)) = (mayby_ip, &self.asn_mmdb) { + // try simplified mmdb first + let rv = asn_mmdb.lookup_contry(ip); + if let Ok(country) = rv { + sess.asn = country + .country + .and_then(|c| c.iso_code) + .map(|s| s.to_string()); + } + if sess.asn.is_none() { + match asn_mmdb.lookup_asn(ip) { + Ok(asn) => { + trace!("asn for {} is {:?}", ip, asn); + sess.asn = asn + .autonomous_system_organization + .map(|s| s.to_string()); + } + Err(e) => { + trace!("failed to lookup ASN for {}: {}", ip, e); + } + } + } + } + + if r.apply(sess) { info!( "matched {} to target {}[{}]", - &sess_dup, + &sess, r.target(), r.type_name() ); - debug!("matched rule details: {}", r); return (r.target(), Some(r)); } } @@ -309,9 +330,160 @@ pub fn map_rule_type( .clone(), )), None => { - unreachable!("you shouldn't next rule-set within another rule-set") + // this is called in remote rule provider with no rule provider + // registry, in this case, we should panic + unreachable!("you shouldn't nest rule-set within another rule-set") } }, RuleType::Match { target } => Box::new(Final { target }), } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use anyhow::Ok; + + use crate::{ + app::dns::{MockClashResolver, SystemResolver}, + common::{geodata::GeoData, http::new_http_client, mmdb::Mmdb}, + config::internal::rule::RuleType, + session::Session, + }; + + const GEO_DATA_DOWNLOAD_URL:&str = "https://github.com/Watfaq/v2ray-rules-dat/releases/download/test/geosite.dat"; + const MMDB_DOWNLOAD_URL:&str = "https://github.com/Loyalsoldier/geoip/releases/download/202307271745/Country.mmdb"; + + #[tokio::test] + async fn test_route_match() { + let mut mock_resolver = MockClashResolver::new(); + mock_resolver.expect_resolve().returning(|host, _| { + if host == "china.com" { + Ok(Some("114.114.114.114".parse().unwrap())) + } else if host == "t.me" { + Ok(Some("149.154.0.1".parse().unwrap())) + } else if host == "git.io" { + Ok(Some("8.8.8.8".parse().unwrap())) + } else { + Ok(None) + } + }); + let mock_resolver = Arc::new(mock_resolver); + + let real_resolver = Arc::new(SystemResolver::new(false).unwrap()); + + let client = new_http_client(real_resolver.clone()).unwrap(); + + let temp_dir = tempfile::tempdir().unwrap(); + + let mmdb = Mmdb::new( + temp_dir.path().join("mmdb.mmdb"), + Some(MMDB_DOWNLOAD_URL.to_string()), + client, + ) + .await + .unwrap(); + + let client = new_http_client(real_resolver.clone()).unwrap(); + + let geodata = GeoData::new( + temp_dir.path().join("geodata.geodata"), + Some(GEO_DATA_DOWNLOAD_URL.to_string()), + client, + ) + .await + .unwrap(); + + let router = super::Router::new( + vec![ + RuleType::GeoIP { + target: "DIRECT".to_string(), + country_code: "CN".to_string(), + no_resolve: false, + }, + RuleType::DomainSuffix { + domain_suffix: "t.me".to_string(), + target: "DS".to_string(), + }, + RuleType::IpCidr { + ipnet: "149.154.0.0/16".parse().unwrap(), + target: "IC".to_string(), + no_resolve: false, + }, + RuleType::DomainSuffix { + domain_suffix: "git.io".to_string(), + target: "DS2".to_string(), + }, + ], + Default::default(), + mock_resolver, + Arc::new(mmdb), + None, + Arc::new(geodata), + temp_dir.path().to_str().unwrap().to_string(), + ) + .await; + + assert_eq!( + router + .match_route(&mut Session { + destination: crate::session::SocksAddr::Domain( + "china.com".to_string(), + 1111, + ), + ..Default::default() + }) + .await + .0, + "DIRECT", + "should resolve and match IP" + ); + + assert_eq!( + router + .match_route(&mut Session { + destination: crate::session::SocksAddr::Domain( + "t.me".to_string(), + 1111, + ), + ..Default::default() + }) + .await + .0, + "DS", + "should match domain" + ); + + assert_eq!( + router + .match_route(&mut Session { + destination: crate::session::SocksAddr::Domain( + "git.io".to_string(), + 1111 + ), + ..Default::default() + }) + .await + .0, + "DS2", + "should still match domain after previous rule resolved IP and non \ + match" + ); + + assert_eq!( + router + .match_route(&mut Session { + destination: crate::session::SocksAddr::Domain( + "no-match".to_string(), + 1111 + ), + ..Default::default() + }) + .await + .0, + "MATCH", + "should fallback to MATCH when nothing matched" + ); + } +} diff --git a/clash_lib/src/app/router/rules/geoip.rs b/clash_lib/src/app/router/rules/geoip.rs index 63ea4278..deff1b8d 100644 --- a/clash_lib/src/app/router/rules/geoip.rs +++ b/clash_lib/src/app/router/rules/geoip.rs @@ -22,9 +22,14 @@ impl std::fmt::Display for GeoIP { impl RuleMatcher for GeoIP { fn apply(&self, sess: &Session) -> bool { - match sess.destination { - crate::session::SocksAddr::Ip(addr) => match self.mmdb.lookup(addr.ip()) - { + let ip = if self.no_resolve { + sess.destination.ip() + } else { + sess.resolved_ip + }; + + if let Some(ip) = ip { + match self.mmdb.lookup_contry(ip) { Ok(country) => { country .country @@ -37,8 +42,9 @@ impl RuleMatcher for GeoIP { debug!("GeoIP lookup failed: {}", e); false } - }, - crate::session::SocksAddr::Domain(..) => false, + } + } else { + false } } diff --git a/clash_lib/src/app/router/rules/ipcidr.rs b/clash_lib/src/app/router/rules/ipcidr.rs index 179e2eaf..793bfe5b 100644 --- a/clash_lib/src/app/router/rules/ipcidr.rs +++ b/clash_lib/src/app/router/rules/ipcidr.rs @@ -1,7 +1,4 @@ -use crate::{ - app::router::rules::RuleMatcher, - session::{Session, SocksAddr}, -}; +use crate::{app::router::rules::RuleMatcher, session::Session}; #[derive(Clone)] pub struct IpCidr { @@ -25,12 +22,20 @@ impl std::fmt::Display for IpCidr { impl RuleMatcher for IpCidr { fn apply(&self, sess: &Session) -> bool { - match self.match_src { - true => self.ipnet.contains(&sess.source.ip()), - false => match &sess.destination { - SocksAddr::Ip(ip) => self.ipnet.contains(&ip.ip()), - SocksAddr::Domain(..) => false, - }, + if self.match_src { + self.ipnet.contains(&sess.source.ip()) + } else { + let ip = if self.no_resolve { + sess.destination.ip() + } else { + sess.resolved_ip + }; + + if let Some(ip) = ip { + self.ipnet.contains(&ip) + } else { + false + } } } diff --git a/clash_lib/src/common/io.rs b/clash_lib/src/common/io.rs index b3f3beb4..cdc45d9c 100644 --- a/clash_lib/src/common/io.rs +++ b/clash_lib/src/common/io.rs @@ -191,7 +191,7 @@ struct CopyBidirectional<'a, A: ?Sized, B: ?Sized> { b_to_a_timeout_duration: Duration, } -impl<'a, A, B> Future for CopyBidirectional<'a, A, B> +impl Future for CopyBidirectional<'_, A, B> where A: AsyncRead + AsyncWrite + Unpin + ?Sized, B: AsyncRead + AsyncWrite + Unpin + ?Sized, diff --git a/clash_lib/src/common/mmdb.rs b/clash_lib/src/common/mmdb.rs index 5c9515fe..116fead6 100644 --- a/clash_lib/src/common/mmdb.rs +++ b/clash_lib/src/common/mmdb.rs @@ -92,9 +92,13 @@ impl Mmdb { } } - pub fn lookup(&self, ip: IpAddr) -> std::io::Result { + pub fn lookup_contry(&self, ip: IpAddr) -> std::io::Result { self.reader .lookup::(ip) .map_err(map_io_error) } + + pub fn lookup_asn(&self, ip: IpAddr) -> std::io::Result { + self.reader.lookup::(ip).map_err(map_io_error) + } } diff --git a/clash_lib/src/common/mod.rs b/clash_lib/src/common/mod.rs index 661a0ce9..bf787f36 100644 --- a/clash_lib/src/common/mod.rs +++ b/clash_lib/src/common/mod.rs @@ -6,6 +6,7 @@ pub mod geodata; pub mod http; pub mod io; pub mod mmdb; +pub mod succinct_set; pub mod timed_future; pub mod tls; pub mod trie; diff --git a/clash_lib/src/common/succinct_set.rs b/clash_lib/src/common/succinct_set.rs new file mode 100644 index 00000000..5d3df7ff --- /dev/null +++ b/clash_lib/src/common/succinct_set.rs @@ -0,0 +1,424 @@ +//! idea: https://github.com/openacid/succinct +//! impl: https://github.com/MetaCubeX/mihomo/blob/Meta/component/trie/domain_set.go +//! I have not idea what's going on here, just copy the code from above link. + +use super::trie::StringTrie; + +static COMPLEX_WILDCARD: u8 = b'+'; +static WILDCARD: u8 = b'*'; +static DOMAIN_STEP: u8 = b'.'; + +#[derive(Default)] +pub struct DomainSet { + leaves: Vec, + label_bit_map: Vec, + labels: Vec, + ranks: Vec, + selects: Vec, +} + +impl DomainSet { + pub fn has(&self, key: &str) -> bool { + let key = key + .chars() + .rev() + .map(|x| x.to_ascii_lowercase()) + .collect::>(); + let mut node_id = 0; + let mut bm_idx = 0; + + struct Cursor { + bm_idx: usize, + index: usize, + } + + let mut stack = vec![]; + + #[derive(PartialEq)] + enum State { + Restart, + Done, + } + + let mut i: usize = 0; + + while i < key.len() + // i++ + { + let mut state = State::Restart; + + 'ctrl: while state == State::Restart { + state = State::Done; + + let c = key[i]; + loop + // bm_idx++ + { + if get_bit(&self.label_bit_map, bm_idx) { + if !stack.is_empty() { + let cursor: Cursor = stack.pop().unwrap(); + let next_node_id = count_zeros( + &self.label_bit_map, + &self.ranks, + cursor.bm_idx + 1, + ); + let mut next_bm_idx = select_ith_one( + &self.label_bit_map, + &self.ranks, + &self.selects, + next_node_id - 1, + ) + 1; + + let mut j = cursor.index; + while j < key.len() && key[j] != DOMAIN_STEP as char { + j += 1; + } + if j == key.len() { + if get_bit(&self.leaves, next_node_id as isize) { + return true; + } else { + state = State::Restart; + continue 'ctrl; + } + } + + while next_bm_idx - next_node_id < self.labels.len() { + if self.labels[next_bm_idx - next_node_id] + == DOMAIN_STEP + { + bm_idx = next_bm_idx as isize; + node_id = next_node_id; + i = j; + + state = State::Restart; + continue 'ctrl; + } + next_bm_idx += 1; + } + } + return false; + } + + if self.labels[bm_idx as usize - node_id] == COMPLEX_WILDCARD { + return true; + } else if self.labels[bm_idx as usize - node_id] == WILDCARD { + let cursor = Cursor { + bm_idx: bm_idx as usize, + index: i, + }; + stack.push(cursor); + } else if self.labels[bm_idx as usize - node_id] == c as u8 { + break; + } + + bm_idx += 1; + } + + node_id = count_zeros( + &self.label_bit_map, + &self.ranks, + bm_idx as usize + 1, + ); + bm_idx = select_ith_one( + &self.label_bit_map, + &self.ranks, + &self.selects, + node_id - 1, + ) as isize + + 1; + + i += 1; + } + } + + get_bit(&self.leaves, node_id as isize) + } + + #[cfg(test)] + pub fn traverse(&self, mut f: F) + where + F: FnMut(&String) -> bool, + { + self.keys(|x| f(&x.chars().rev().collect::())); + } +} + +impl DomainSet { + fn init(&mut self) { + self.ranks.push(0); + for i in 0..self.label_bit_map.len() { + let n = self.label_bit_map[i].count_ones(); + self.ranks.push(self.ranks.last().unwrap() + n as i32); + } + + let mut n = 0; + for i in 0..self.label_bit_map.len() << 6 { + let z = self.label_bit_map[i >> 6] >> (i & 63) & 1; + if z == 1 && n & 63 == 0 { + self.selects.push(i as i32); + } + n += z; + } + } + + #[cfg(test)] + fn keys(&self, mut f: F) + where + F: FnMut(&String) -> bool, + { + let mut current_key = vec![]; + + fn traverse( + this: &DomainSet, + current_key: &mut Vec, + node_id: isize, + bm_idx: isize, + f: &mut F, + ) -> bool + where + F: FnMut(&String) -> bool, + { + if get_bit(&this.leaves, node_id) && !f(¤t_key.iter().collect()) { + return false; + } + + let mut bm_idx = bm_idx; + + loop { + if get_bit(&this.label_bit_map, bm_idx) { + return true; + } + + let next_label = this.labels[(bm_idx - node_id) as usize]; + current_key.push(next_label as char); + let next_node_id = count_zeros( + &this.label_bit_map, + &this.ranks, + bm_idx as usize + 1, + ); + let next_bm_idx = select_ith_one( + &this.label_bit_map, + &this.ranks, + &this.selects, + next_node_id - 1, + ) + 1; + + if !traverse( + this, + current_key, + next_node_id as isize, + next_bm_idx as isize, + f, + ) { + return false; + } + + current_key.pop(); + + bm_idx += 1; + } + } + + traverse(self, &mut current_key, 0, 0, &mut f); + } +} + +struct QElt { + s: usize, + e: usize, + col: usize, +} + +/// Convert a `StringTrie` to a `DomainSet`. +/// TODO: support loading from a binary file. +/// e.g. the so called 'mrs' file in the MiHoMo project. +impl From> for DomainSet { + fn from(value: StringTrie) -> Self { + let mut keys = vec![]; + value.traverse(|key, _| { + keys.push(key.chars().rev().collect::()); + true + }); + keys.sort(); + + let mut rv = DomainSet::default(); + + let mut l_idx = 0; + + let mut queue = vec![QElt { + s: 0, + e: keys.len(), + col: 0, + }]; + + let mut i = 0; + loop { + let elt = &mut queue[i]; + if elt.col == keys[elt.s].len() { + elt.s += 1; + set_bit(&mut rv.leaves, i, true); + } + + let mut j = elt.s; + let e = elt.e; + let col = elt.col; + while j < e { + let frm = j; + while j < e && keys[j].chars().nth(col) == keys[frm].chars().nth(col) + { + j += 1; + } + + queue.push(QElt { + s: frm, + e: j, + col: col + 1, + }); + rv.labels.push(keys[frm].chars().nth(col).unwrap() as u8); + set_bit(&mut rv.label_bit_map, l_idx, false); + l_idx += 1; + } + + set_bit(&mut rv.label_bit_map, l_idx, true); + l_idx += 1; + + if i == queue.len() - 1 { + break; + } + i += 1; + } + + rv.init(); + + rv + } +} + +fn get_bit(bm: &[u64], i: isize) -> bool { + bm[(i >> 6) as usize] & (1 << (i & 63) as usize) != 0 +} + +fn set_bit(bm: &mut Vec, i: usize, v: bool) { + while i >> 6 >= (bm.len()) { + bm.push(0); + } + bm[i >> 6] |= (v as u64) << (i & 63); +} + +fn count_zeros(bm: &[u64], ranks: &[i32], i: usize) -> usize { + i - ranks[i >> 6] as usize + - (bm[i >> 6] & ((1 << (i & 63)) - 1)).count_ones() as usize +} + +fn select_ith_one(bm: &[u64], ranks: &[i32], selects: &[i32], i: usize) -> usize { + let base = selects[i >> 6] & !63; + let mut find_ith_one = i as isize - ranks[base as usize >> 6] as isize; + for (i, w) in bm.iter().enumerate().skip(base as usize >> 6) { + let mut bit_idx = 0; + let mut w = *w; + while w > 0 { + find_ith_one -= (w & 1) as isize; + if find_ith_one < 0 { + return (i << 6) + bit_idx; + } + + let t0 = (w & !1).trailing_zeros(); + w = w.unbounded_shr(t0); + bit_idx += t0 as usize; + } + } + + unreachable!("invalid data"); +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + #[test] + fn test_domain_set_complex_wildcard() { + let mut tree = super::StringTrie::new(); + let domains = vec![ + "baidu.com", + "google.com", + "www.google.com", + "test.a.net", + "test.a.oc", + "mijia cloud", + ".qq.com", + "+.cn", + ]; + + for d in domains { + tree.insert(d, Arc::new(true)); + } + + let mut key_src = vec![]; + tree.traverse(|key, _| { + key_src.push(key.to_owned()); + true + }); + key_src.sort(); + + let set = super::DomainSet::from(tree); + assert!(set.has("test.cn")); + assert!(set.has("cn")); + assert!(set.has("mijia cloud")); + assert!(set.has("test.a.net")); + assert!(set.has("www.qq.com")); + assert!(set.has("google.com")); + assert!(!set.has("qq.com")); + assert!(!set.has("www.baidu.com")); + + test_dump(&key_src, &set); + } + + #[test] + fn test_domain_set_wildcard() { + let mut tree = super::StringTrie::new(); + let domains = vec![ + "*.*.*.baidu.com", + "www.baidu.*", + "stun.*.*", + "*.*.qq.com", + "test.*.baidu.com", + "*.apple.com", + ]; + + for d in domains { + tree.insert(d, Arc::new(true)); + } + + let mut key_src = vec![]; + tree.traverse(|key, _| { + key_src.push(key.to_owned()); + true + }); + key_src.sort(); + + let set = super::DomainSet::from(tree); + + assert!(set.has("www.baidu.com")); + assert!(set.has("test.test.baidu.com")); + assert!(set.has("test.test.qq.com")); + assert!(set.has("stun.ab.cd")); + assert!(!set.has("test.baidu.com")); + assert!(!set.has("www.google.com")); + assert!(!set.has("a.www.google.com")); + assert!(!set.has("test.qq.com")); + assert!(!set.has("test.test.test.qq.com")); + + test_dump(&key_src, &set); + } + + fn test_dump(data_src: &Vec, set: &super::DomainSet) { + let mut data_set = vec![]; + set.traverse(|key| { + data_set.push(key.to_owned()); + true + }); + data_set.sort(); + + assert_eq!(data_src, &data_set); + } +} diff --git a/clash_lib/src/common/trie.rs b/clash_lib/src/common/trie.rs index 029baa7c..25fade81 100644 --- a/clash_lib/src/common/trie.rs +++ b/clash_lib/src/common/trie.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use std::{collections::HashMap, sync::Arc}; static DOMAIN_STEP: &str = "."; @@ -7,26 +5,18 @@ static COMPLEX_WILDCARD: &str = "+"; static DOT_WILDCARD: &str = ""; static WILDCARD: &str = "*"; -#[derive(Clone)] -pub struct StringTrie { - root: Node, - __type_holder: PhantomData, -} - -#[derive(Clone)] -pub struct Node { +pub struct Node { children: HashMap>, - // TODO: maybe we only need RefCell here data: Option>, } -impl Default for Node { +impl Default for Node { fn default() -> Self { Self::new() } } -impl Node { +impl Node { pub fn new() -> Self { Node { children: HashMap::new(), @@ -53,20 +43,24 @@ impl Node { pub fn add_child(&mut self, s: &str, child: Node) { self.children.insert(s.to_string(), child); } + + pub fn get_children(&self) -> &HashMap> { + &self.children + } +} +pub struct StringTrie { + root: Node, } -impl Default for StringTrie { +impl Default for StringTrie { fn default() -> Self { Self::new() } } -impl StringTrie { +impl StringTrie { pub fn new() -> Self { - StringTrie { - root: Node::new(), - __type_holder: PhantomData, - } + StringTrie { root: Node::new() } } pub fn insert(&mut self, domain: &str, data: Arc) -> bool { @@ -109,6 +103,53 @@ impl StringTrie { None } + pub fn traverse(&self, mut f: F) + where + F: FnMut(&String, &T) -> bool, + { + for (key, child) in self.root.get_children() { + Self::traverse_inner(&[key], child, &mut f); + if let Some(data) = child.get_data() { + if !f(key, data) { + return; + } + } + } + } + + fn traverse_inner<'a, F>( + keys: &'a [&String], + node: &'a Node, + f: &mut F, + ) -> bool + where + F: FnMut(&String, &T) -> bool, + { + for (key, child) in node.get_children() { + let keys = [&[key], keys].concat(); + + let d = keys.iter().map(|x| x.as_str()).collect::>(); + if let Some(data) = child.get_data() { + let domain = d.join(DOMAIN_STEP); + let key = if domain.starts_with(DOMAIN_STEP) { + COMPLEX_WILDCARD.to_string() + domain.as_str() + } else { + domain + }; + + if !f(&key, data) { + return false; + } + } + + if !Self::traverse_inner(&keys, child, f) { + return false; + } + } + + true + } + fn insert_inner(&mut self, parts: &[&str], data: Arc) { let mut node = &mut self.root; diff --git a/clash_lib/src/common/utils.rs b/clash_lib/src/common/utils.rs index f8bf9d96..f121a191 100644 --- a/clash_lib/src/common/utils.rs +++ b/clash_lib/src/common/utils.rs @@ -111,35 +111,4 @@ where Ok(()) } -use anyhow::Context; -use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; -use std::fs; - use super::http::HttpClient; - -pub fn load_cert_chain( - cert_path: &Path, -) -> anyhow::Result>> { - let cert_chain = - fs::read(cert_path).context("failed to read certificate chain")?; - let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") { - vec![CertificateDer::from(cert_chain)] - } else { - rustls_pemfile::certs(&mut &*cert_chain) - .collect::>() - .context("invalid PEM-encoded certificate")? - }; - Ok(cert_chain) -} - -pub fn load_priv_key(key_path: &Path) -> anyhow::Result> { - let key = fs::read(key_path).context("failed to read private key")?; - let key = if key_path.extension().map_or(false, |x| x == "der") { - PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key)) - } else { - rustls_pemfile::private_key(&mut &*key) - .context("malformed PKCS #1 private key")? - .ok_or_else(|| anyhow::Error::msg("no private keys found"))? - }; - Ok(key) -} diff --git a/clash_lib/src/config/def.rs b/clash_lib/src/config/def.rs index 4e6ca7c6..948df383 100644 --- a/clash_lib/src/config/def.rs +++ b/clash_lib/src/config/def.rs @@ -12,6 +12,7 @@ fn default_tun_address() -> String { #[serde(rename_all = "kebab-case")] pub struct TunConfig { pub enable: bool, + #[serde(alias = "device-url")] pub device_id: String, /// tun interface address #[serde(default = "default_tun_address")] @@ -20,6 +21,10 @@ pub struct TunConfig { #[serde(default)] pub route_all: bool, pub mtu: Option, + /// fwmark on Linux only + pub so_mark: Option, + /// policy routing table on Linux only + pub route_table: Option, } #[derive(Serialize, Deserialize, Default, Copy, Clone)] @@ -76,11 +81,11 @@ impl Display for LogLevel { /// port: 8888 /// socks-port: 8889 /// mixed-port: 8899 - +/// /// tun: /// enable: false /// device-id: "dev://utun1989" - +/// /// dns: /// enable: true /// listen: 127.0.0.1:53553 @@ -88,9 +93,9 @@ impl Display for LogLevel { /// # tcp: 127.0.0.1:53553 /// # dot: 127.0.0.1:53554 /// # doh: 127.0.0.1:53555 - +/// /// # ipv6: false # when the false, response to AAAA questions will be empty - +/// /// # These nameservers are used to resolve the DNS nameserver hostnames /// below. # Specify IP addresses only /// default-nameserver: @@ -99,14 +104,14 @@ impl Display for LogLevel { /// enhanced-mode: fake-ip /// fake-ip-range: 198.18.0.2/16 # Fake IP addresses pool CIDR /// # use-hosts: true # lookup hosts and return IP record - +/// /// # Hostnames in this list will not be resolved with fake IPs /// # i.e. questions to these domain names will always be answered with their /// # real IP addresses /// # fake-ip-filter: /// # - '*.lan' /// # - localhost.ptlogin2.qq.com - +/// /// # Supports UDP, TCP, DoT, DoH. You can specify the port to connect to. /// # All DNS questions are sent directly to the nameserver, without proxies /// # involved. Clash answers the DNS question with the first result gathered. @@ -116,7 +121,7 @@ impl Display for LogLevel { /// - tls://1.1.1.1:853 # DNS over TLS /// - https://1.1.1.1/dns-query # DNS over HTTPS /// # - dhcp://en0 # dns from dhcp - +/// /// allow-lan: true /// mode: rule /// log-level: debug @@ -125,11 +130,11 @@ impl Display for LogLevel { /// # secret: "clash-rs" /// experimental: /// ignore-resolve-fail: true - +/// /// profile: /// store-selected: true /// store-fake-ip: false - +/// /// proxy-groups: /// - name: "relay" type: relay proxies: /// - "plain-vmess" @@ -139,24 +144,24 @@ impl Display for LogLevel { /// - "load-balance" /// - "select" /// - DIRECT - +/// /// - name: "relay-one" type: relay use: /// - "file-provider" - +/// /// - name: "auto" type: url-test use: /// - "file-provider" /// proxies: /// - DIRECT /// url: "http://www.gstatic.com/generate_204" /// interval: 300 - +/// /// - name: "fallback-auto" type: fallback use: /// - "file-provider" /// proxies: /// - DIRECT /// url: "http://www.gstatic.com/generate_204" /// interval: 300 - +/// /// - name: "load-balance" type: load-balance use: /// - "file-provider" /// proxies: @@ -164,15 +169,15 @@ impl Display for LogLevel { /// strategy: round-robin /// url: "http://www.gstatic.com/generate_204" /// interval: 300 - +/// /// - name: select type: select use: /// - "file-provider" - +/// /// - name: test 🌏 type: select use: /// - "file-provider" /// proxies: /// - DIRECT - +/// /// proxies: /// - name: plain-vmess type: vmess server: 10.0.0.13 port: 16823 uuid: /// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true @@ -181,15 +186,15 @@ impl Display for LogLevel { /// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true /// skip-cert-verify: true network: ws ws-opts: path: /// /api/v3/download.getFile headers: Host: www.amazon.com - +/// /// - name: tls-vmess type: vmess server: 10.0.0.13 port: 8443 uuid: /// 23ad6b10-8d1a-40f7-8ad0-e3e35cd38297 alterId: 0 cipher: auto udp: true /// skip-cert-verify: true tls: true - +/// /// - name: h2-vmess type: vmess server: 10.0.0.13 port: 8444 uuid: /// b831381d-6324-4d53-ad4f-8cda48b30811 alterId: 0 cipher: auto udp: true /// skip-cert-verify: true tls: true network: h2 h2-opts: path: /ray - +/// /// - name: vmess-altid type: vmess server: tw-1.ac.laowanxiang.com port: 153 /// uuid: 46dd0dd3-2cc0-3f55-907c-d94e54877687 alterId: 64 cipher: auto udp: /// true network: ws ws-opts: path: /api/v3/download.getFile headers: Host: @@ -201,7 +206,7 @@ impl Display for LogLevel { /// - h2 /// - http/1.1 /// skip-cert-verify: true - +/// /// proxy-providers: /// file-provider: /// type: file @@ -211,14 +216,14 @@ impl Display for LogLevel { /// enable: true /// url: http://www.gstatic.com/generate_204 /// interval: 300 - +/// /// rule-providers: /// file-provider: /// type: file /// path: ./rule-set.yaml /// interval: 300 /// behavior: domain - +/// /// rules: /// - DOMAIN,ipinfo.io,relay /// - RULE-SET,file-provider,trojan @@ -243,7 +248,6 @@ pub struct Config { /// The redir port #[doc(hidden)] pub redir_port: Option, - #[doc(hidden)] pub tproxy_port: Option, /// The HTTP/SOCKS5 mixed proxy port /// # Example @@ -290,6 +294,10 @@ pub struct Config { pub mmdb: String, /// Country database download url pub mmdb_download_url: Option, + /// Optional ASN database path relative to the $CWD + pub asn_mmdb: String, + /// Optional ASN database download url + pub asn_mmdb_download_url: Option, /// Geosite database path relative to the $CWD pub geosite: String, /// Geosite database download url @@ -393,6 +401,8 @@ impl Default for Config { "https://github.com/Loyalsoldier/geoip/releases/download/202307271745/Country.mmdb" .to_owned(), ), + asn_mmdb: "Country-asn.mmdb".to_string(), + asn_mmdb_download_url: None, // can be downloaded from the same release but let's not make it default geosite: "geosite.dat".to_string(), geosite_download_url: Some("https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/202406182210/geosite.dat".to_owned()), tun: Default::default(), @@ -404,7 +414,7 @@ impl Default for Config { #[serde(untagged)] pub enum DNSListen { Udp(String), - Multiple(HashMap), + Multiple(HashMap), } /// DNS client/server settings @@ -415,10 +425,17 @@ pub enum DNSListen { /// enable: true /// ipv6: false # when the false, response to AAAA questions will be empty /// listen: -/// udp: 127.0.0.1:5353 -/// tcp: 127.0.0.1:5353 -/// doh: 127.0.0.1:5354 -/// dot: 127.0.0.1:5355 +/// udp: 127.0.0.1:53553 +/// tcp: 127.0.0.1:53553 +/// dot: +/// addr: 127.0.0.1:53554 +/// hostname: dns.clash +/// ca-cert: dns.crt +/// ca-key: dns.key +/// doh: +/// addr: 127.0.0.1:53555 +/// ca-cert: dns.crt +/// ca-key: dns.key /// ``` #[derive(Serialize, Deserialize)] diff --git a/clash_lib/src/config/internal/config.rs b/clash_lib/src/config/internal/config.rs index f8dd7048..d10cc194 100644 --- a/clash_lib/src/config/internal/config.rs +++ b/clash_lib/src/config/internal/config.rs @@ -92,6 +92,8 @@ impl TryFrom for Config { routing_mask: c.routing_mask, mmdb: c.mmdb.to_owned(), mmdb_download_url: c.mmdb_download_url.to_owned(), + asn_mmdb: c.asn_mmdb.to_owned(), + asn_mmdb_download_url: c.asn_mmdb_download_url.to_owned(), geosite: c.geosite.to_owned(), geosite_download_url: c.geosite_download_url.to_owned(), }, @@ -118,6 +120,8 @@ impl TryFrom for Config { Error::InvalidConfig(format!("parse tun gateway: {}", x)) })?, mtu: t.mtu, + so_mark: t.so_mark, + route_table: t.route_table, }, None => TunConfig::default(), }, @@ -281,6 +285,8 @@ pub struct General { pub routing_mask: Option, pub mmdb: String, pub mmdb_download_url: Option, + pub asn_mmdb: String, + pub asn_mmdb_download_url: Option, pub geosite: String, pub geosite_download_url: Option, @@ -300,6 +306,8 @@ pub struct TunConfig { pub routes: Vec, pub gateway: IpNet, pub mtu: Option, + pub so_mark: Option, + pub route_table: Option, } #[derive(Clone, Default)] @@ -351,7 +359,7 @@ pub struct Inbound { pub bind_address: BindAddress, } -#[derive(Serialize, Deserialize, Default)] +#[derive(Serialize, Deserialize, Default, Clone)] pub struct Controller { pub external_controller: Option, pub external_ui: Option, diff --git a/clash_lib/src/config/internal/proxy.rs b/clash_lib/src/config/internal/proxy.rs index 666761ad..46436ecf 100644 --- a/clash_lib/src/config/internal/proxy.rs +++ b/clash_lib/src/config/internal/proxy.rs @@ -62,6 +62,7 @@ pub enum OutboundProxyProtocol { Vmess(OutboundVmess), #[serde(rename = "wireguard")] Wireguard(OutboundWireguard), + #[cfg(feature = "onion")] #[serde(rename = "tor")] Tor(OutboundTor), #[cfg(feature = "tuic")] @@ -84,6 +85,7 @@ impl OutboundProxyProtocol { OutboundProxyProtocol::Wireguard(wireguard) => { &wireguard.common_opts.name } + #[cfg(feature = "onion")] OutboundProxyProtocol::Tor(tor) => &tor.name, #[cfg(feature = "tuic")] OutboundProxyProtocol::Tuic(tuic) => &tuic.common_opts.name, @@ -119,6 +121,7 @@ impl Display for OutboundProxyProtocol { OutboundProxyProtocol::Trojan(_) => write!(f, "Trojan"), OutboundProxyProtocol::Vmess(_) => write!(f, "Vmess"), OutboundProxyProtocol::Wireguard(_) => write!(f, "Wireguard"), + #[cfg(feature = "onion")] OutboundProxyProtocol::Tor(_) => write!(f, "Tor"), #[cfg(feature = "tuic")] OutboundProxyProtocol::Tuic(_) => write!(f, "Tuic"), @@ -129,7 +132,7 @@ impl Display for OutboundProxyProtocol { #[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)] #[serde(rename_all = "kebab-case")] -pub struct CommonOption { +pub struct CommonConfigOptions { pub name: String, pub server: String, pub port: u16, @@ -145,7 +148,7 @@ pub struct CommonOption { #[serde(rename_all = "kebab-case")] pub struct OutboundShadowsocks { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub cipher: String, pub password: String, #[serde(default = "default_bool_true")] @@ -158,7 +161,7 @@ pub struct OutboundShadowsocks { #[serde(rename_all = "kebab-case")] pub struct OutboundSocks5 { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub username: Option, pub password: Option, #[serde(default = "Default::default")] @@ -195,7 +198,7 @@ pub struct GrpcOpt { #[serde(rename_all = "kebab-case")] pub struct OutboundTrojan { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub password: String, pub alpn: Option>, pub sni: Option, @@ -210,7 +213,7 @@ pub struct OutboundTrojan { #[serde(rename_all = "kebab-case")] pub struct OutboundVmess { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub uuid: String, #[serde(alias = "alterId")] pub alter_id: u16, @@ -230,7 +233,7 @@ pub struct OutboundVmess { #[serde(rename_all = "kebab-case")] pub struct OutboundWireguard { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub private_key: String, pub public_key: String, pub preshared_key: Option, @@ -254,7 +257,7 @@ pub struct OutboundTor { #[serde(rename_all = "kebab-case")] pub struct OutboundTuic { #[serde(flatten)] - pub common_opts: CommonOption, + pub common_opts: CommonConfigOptions, pub uuid: Uuid, pub password: String, /// override field 'server' dns record, not used for now diff --git a/clash_lib/src/lib.rs b/clash_lib/src/lib.rs index 838b65ef..eed31a3d 100644 --- a/clash_lib/src/lib.rs +++ b/clash_lib/src/lib.rs @@ -1,5 +1,6 @@ #![feature(ip)] #![feature(sync_unsafe_cell)] +#![feature(unbounded_shifts)] #[macro_use] extern crate anyhow; @@ -14,7 +15,11 @@ use crate::{ internal::{proxy::OutboundProxy, InternalConfig}, }, }; -use app::{dispatcher::StatisticsManager, dns::SystemResolver, profile}; +use app::{ + dispatcher::StatisticsManager, + dns::{SystemResolver, ThreadSafeDNSResolver}, + profile, +}; use common::{auth, http::new_http_client, mmdb}; use config::def::LogLevel; use once_cell::sync::OnceCell; @@ -95,7 +100,9 @@ impl Config { pub struct GlobalState { log_level: LogLevel, - inbound_listener_handle: Option>>, + // must be Some otherwise we'll refuse to start + inbound_listener_handle: JoinHandle>, + tunnel_listener_handle: Option>>, api_listener_handle: Option>>, dns_listener_handle: Option>>, @@ -170,126 +177,23 @@ async fn start_async(opts: Options) -> Result<(), Error> { let cwd = PathBuf::from(cwd); - debug!("initializing cache store"); - let cache_store = profile::ThreadSafeCacheFile::new( - cwd.join("cache.db").as_path().to_str().unwrap(), - config.profile.store_selected, - ); + // things we need to clone before consuming config + let controller_cfg = config.general.controller.clone(); + let log_level = config.general.log_level; - debug!("initializing dns resolver"); - let system_resolver = Arc::new( - SystemResolver::new(config.general.ipv6 && config.dns.ipv6) - .map_err(|x| Error::DNSError(x.to_string()))?, - ); - let client = new_http_client(system_resolver.clone()) - .map_err(|x| Error::DNSError(x.to_string()))?; - - debug!("initializing mmdb"); - let mmdb = Arc::new( - mmdb::Mmdb::new( - cwd.join(&config.general.mmdb), - config.general.mmdb_download_url, - client, - ) - .await?, - ); - - let dns_resolver = dns::new_resolver( - &config.dns, - Some(cache_store.clone()), - Some(mmdb.clone()), - ) - .await; - - debug!("initializing outbound manager"); - let outbound_manager = Arc::new( - OutboundManager::new( - config - .proxies - .into_values() - .filter_map(|x| match x { - OutboundProxy::ProxyServer(s) => Some(s), - _ => None, - }) - .collect(), - config - .proxy_groups - .into_values() - .filter_map(|x| match x { - OutboundProxy::ProxyGroup(g) => Some(g), - _ => None, - }) - .collect(), - config.proxy_providers, - config.proxy_names, - dns_resolver.clone(), - cache_store.clone(), - cwd.to_string_lossy().to_string(), - ) - .await?, - ); + let components = create_components(cwd.clone(), config).await?; - debug!("initializing router"); - let client = new_http_client(system_resolver) - .map_err(|x| Error::DNSError(x.to_string()))?; - let geodata = Arc::new( - geodata::GeoData::new( - cwd.join(&config.general.geosite), - config.general.geosite_download_url, - client, - ) - .await?, - ); - - let router = Arc::new( - Router::new( - config.rules, - config.rule_providers, - dns_resolver.clone(), - mmdb, - geodata, - cwd.to_string_lossy().to_string(), - ) - .await, - ); - - let statistics_manager = StatisticsManager::new(); - - let dispatcher = Arc::new(Dispatcher::new( - outbound_manager.clone(), - router.clone(), - dns_resolver.clone(), - config.general.mode, - statistics_manager.clone(), - )); - - let authenticator = Arc::new(auth::PlainAuthenticator::new(config.users)); - - debug!("initializing inbound manager"); - let inbound_manager = Arc::new(Mutex::new(InboundManager::new( - config.general.inbound, - dispatcher.clone(), - authenticator, - )?)); - - let inbound_runner = inbound_manager.lock().await.get_runner()?; + let inbound_runner = components.inbound_manager.lock().await.get_runner()?; let inbound_listener_handle = tokio::spawn(inbound_runner); - let tun_runner = - get_tun_runner(config.tun, dispatcher.clone(), dns_resolver.clone())?; - let tun_runner_handle = tun_runner.map(tokio::spawn); - - debug!("initializing dns listener"); - let dns_listener_handle = - dns::get_dns_listener(config.dns, dns_resolver.clone()) - .await - .map(tokio::spawn); + let tun_runner_handle = components.tun_runner.map(tokio::spawn); + let dns_listener_handle = components.dns_listener.map(tokio::spawn); let (reload_tx, mut reload_rx) = mpsc::channel(1); let global_state = Arc::new(Mutex::new(GlobalState { - log_level: config.general.log_level, - inbound_listener_handle: Some(inbound_listener_handle), + log_level, + inbound_listener_handle, tunnel_listener_handle: tun_runner_handle, dns_listener_handle, reload_tx, @@ -298,16 +202,16 @@ async fn start_async(opts: Options) -> Result<(), Error> { })); let api_runner = app::api::get_api_runner( - config.general.controller, + controller_cfg, log_tx.clone(), - inbound_manager.clone(), - dispatcher, + components.inbound_manager, + components.dispatcher, global_state.clone(), - dns_resolver, - outbound_manager, - statistics_manager, - cache_store, - router, + components.dns_resolver, + components.outbound_manager, + components.statistics_manager, + components.cache_store, + components.router, cwd.to_string_lossy().to_string(), ); if let Some(r) = api_runner { @@ -341,116 +245,15 @@ async fn start_async(opts: Options) -> Result<(), Error> { } }; - debug!("reloading dns resolver"); - let system_resolver = Arc::new( - SystemResolver::new(config.dns.ipv6) - .map_err(|x| Error::DNSError(x.to_string()))?, - ); - let client = new_http_client(system_resolver.clone()) - .map_err(|x| Error::DNSError(x.to_string()))?; - - debug!("reloading mmdb"); - let mmdb = Arc::new( - mmdb::Mmdb::new( - cwd.join(&config.general.mmdb), - config.general.mmdb_download_url, - client, - ) - .await?, - ); - - let client = new_http_client(system_resolver) - .map_err(|x| Error::DNSError(x.to_string()))?; - let geodata = Arc::new( - geodata::GeoData::new( - cwd.join(&config.general.geosite), - config.general.geosite_download_url, - client, - ) - .await?, - ); - - debug!("reloading cache store"); - let cache_store = profile::ThreadSafeCacheFile::new( - cwd.join("cache.db").as_path().to_str().unwrap(), - config.profile.store_selected, - ); - - let dns_resolver = dns::new_resolver( - &config.dns, - Some(cache_store.clone()), - Some(mmdb.clone()), - ) - .await; - - debug!("reloading outbound manager"); - let outbound_manager = Arc::new( - OutboundManager::new( - config - .proxies - .into_values() - .filter_map(|x| match x { - OutboundProxy::ProxyServer(s) => Some(s), - _ => None, - }) - .collect(), - config - .proxy_groups - .into_values() - .filter_map(|x| match x { - OutboundProxy::ProxyGroup(g) => Some(g), - _ => None, - }) - .collect(), - config.proxy_providers, - config.proxy_names, - dns_resolver.clone(), - cache_store.clone(), - cwd.to_string_lossy().to_string(), - ) - .await?, - ); - - debug!("reloading router"); - let router = Arc::new( - Router::new( - config.rules, - config.rule_providers, - dns_resolver.clone(), - mmdb, - geodata, - cwd.to_string_lossy().to_string(), - ) - .await, - ); - - let statistics_manager = StatisticsManager::new(); - - let dispatcher = Arc::new(Dispatcher::new( - outbound_manager.clone(), - router.clone(), - dns_resolver.clone(), - config.general.mode, - statistics_manager.clone(), - )); - - let authenticator = - Arc::new(auth::PlainAuthenticator::new(config.users)); - - debug!("reloading inbound manager"); - let inbound_manager = Arc::new(Mutex::new(InboundManager::new( - config.general.inbound, - dispatcher.clone(), - authenticator, - )?)); + let controller_cfg = config.general.controller.clone(); + + let new_componenets = create_components(cwd.clone(), config).await?; done.send(()).unwrap(); debug!("stopping listeners"); let mut g = global_state.lock().await; - if let Some(h) = g.inbound_listener_handle.take() { - h.abort(); - } + g.inbound_listener_handle.abort(); if let Some(h) = g.tunnel_listener_handle.take() { h.abort(); } @@ -461,42 +264,37 @@ async fn start_async(opts: Options) -> Result<(), Error> { h.abort(); } - let inbound_listener_handle = inbound_manager + debug!("reloading inbound listener"); + let inbound_listener_handle = new_componenets + .inbound_manager .lock() .await .get_runner() .map(tokio::spawn)?; - let tun_runner_handle = get_tun_runner( - config.tun, - dispatcher.clone(), - dns_resolver.clone(), - )? - .map(tokio::spawn); + debug!("reloading tun runner"); + let tun_runner_handle = new_componenets.tun_runner.map(tokio::spawn); debug!("reloading dns listener"); - let dns_listener_handle = - dns::get_dns_listener(config.dns, dns_resolver.clone()) - .await - .map(tokio::spawn); + let dns_listener_handle = new_componenets.dns_listener.map(tokio::spawn); debug!("reloading api listener"); let api_listener_handle = app::api::get_api_runner( - config.general.controller, + controller_cfg, log_tx.clone(), - inbound_manager.clone(), - dispatcher, + new_componenets.inbound_manager, + new_componenets.dispatcher, global_state.clone(), - dns_resolver, - outbound_manager, - statistics_manager, - cache_store, - router, + new_componenets.dns_resolver, + new_componenets.outbound_manager, + new_componenets.statistics_manager, + new_componenets.cache_store, + new_componenets.router, cwd.to_string_lossy().to_string(), ) .map(tokio::spawn); - g.inbound_listener_handle = Some(inbound_listener_handle); + g.inbound_listener_handle = inbound_listener_handle; g.tunnel_listener_handle = tun_runner_handle; g.dns_listener_handle = dns_listener_handle; g.api_listener_handle = api_listener_handle; @@ -510,10 +308,172 @@ async fn start_async(opts: Options) -> Result<(), Error> { }) } +struct RuntimeComponents { + cache_store: profile::ThreadSafeCacheFile, + dns_resolver: ThreadSafeDNSResolver, + outbound_manager: Arc, + router: Arc, + dispatcher: Arc, + statistics_manager: Arc, + inbound_manager: Arc>, + + tun_runner: Option, + dns_listener: Option, +} + +async fn create_components( + cwd: PathBuf, + config: InternalConfig, +) -> Result { + let system_resolver = Arc::new( + SystemResolver::new(config.dns.ipv6) + .map_err(|x| Error::DNSError(x.to_string()))?, + ); + let client = new_http_client(system_resolver.clone()) + .map_err(|x| Error::DNSError(x.to_string()))?; + + debug!("initializing mmdb"); + let country_mmdb = Arc::new( + mmdb::Mmdb::new( + cwd.join(&config.general.mmdb), + config.general.mmdb_download_url, + client.clone(), + ) + .await?, + ); + + let geodata = Arc::new( + geodata::GeoData::new( + cwd.join(&config.general.geosite), + config.general.geosite_download_url, + client.clone(), + ) + .await?, + ); + + debug!("initializing cache store"); + let cache_store = profile::ThreadSafeCacheFile::new( + cwd.join("cache.db").as_path().to_str().unwrap(), + config.profile.store_selected, + ); + + let dns_listen = config.dns.listen.clone(); + debug!("initializing dns resolver"); + let dns_resolver = dns::new_resolver( + config.dns, + Some(cache_store.clone()), + Some(country_mmdb.clone()), + ) + .await; + + debug!("initializing outbound manager"); + let outbound_manager = Arc::new( + OutboundManager::new( + config + .proxies + .into_values() + .filter_map(|x| match x { + OutboundProxy::ProxyServer(s) => Some(s), + _ => None, + }) + .collect(), + config + .proxy_groups + .into_values() + .filter_map(|x| match x { + OutboundProxy::ProxyGroup(g) => Some(g), + _ => None, + }) + .collect(), + config.proxy_providers, + config.proxy_names, + dns_resolver.clone(), + cache_store.clone(), + cwd.to_string_lossy().to_string(), + ) + .await?, + ); + + debug!("initializing country asn mmdb"); + let p = cwd.join(&config.general.asn_mmdb); + let asn_mmdb = if p.exists() || config.general.asn_mmdb_download_url.is_some() { + Some(Arc::new( + mmdb::Mmdb::new(p, config.general.asn_mmdb_download_url, client.clone()) + .await?, + )) + } else { + None + }; + + debug!("initializing router"); + let router = Arc::new( + Router::new( + config.rules, + config.rule_providers, + dns_resolver.clone(), + country_mmdb, + asn_mmdb, + geodata, + cwd.to_string_lossy().to_string(), + ) + .await, + ); + + let statistics_manager = StatisticsManager::new(); + + debug!("initializing dispatcher"); + let dispatcher = Arc::new(Dispatcher::new( + outbound_manager.clone(), + router.clone(), + dns_resolver.clone(), + config.general.mode, + statistics_manager.clone(), + )); + + debug!("initializing authenticator"); + let authenticator = Arc::new(auth::PlainAuthenticator::new(config.users)); + + debug!("initializing inbound manager"); + let inbound_manager = Arc::new(Mutex::new(InboundManager::new( + config.general.inbound, + dispatcher.clone(), + authenticator, + )?)); + + debug!("initializing tun runner"); + let tun_runner = + get_tun_runner(config.tun, dispatcher.clone(), dns_resolver.clone())?; + + debug!("initializing dns listener"); + let dns_listener = + dns::get_dns_listener(dns_listen, dns_resolver.clone(), &cwd).await; + + Ok(RuntimeComponents { + cache_store, + dns_resolver, + outbound_manager, + router, + dispatcher, + statistics_manager, + inbound_manager, + tun_runner, + dns_listener, + }) +} + #[cfg(test)] mod tests { use crate::{shutdown, start, Config, Options}; - use std::{thread, time::Duration}; + use std::{sync::Once, thread, time::Duration}; + + static INIT: Once = Once::new(); + + #[allow(dead_code)] + pub fn initialize() { + INIT.call_once(|| { + env_logger::init(); + }); + } #[test] fn start_and_stop() { diff --git a/clash_lib/src/proxy/converters/mod.rs b/clash_lib/src/proxy/converters/mod.rs index a55df2b1..5676a69e 100644 --- a/clash_lib/src/proxy/converters/mod.rs +++ b/clash_lib/src/proxy/converters/mod.rs @@ -2,6 +2,7 @@ pub mod hysteria2; #[cfg(feature = "shadowsocks")] pub mod shadowsocks; pub mod socks5; +#[cfg(feature = "onion")] pub mod tor; pub mod trojan; #[cfg(feature = "tuic")] diff --git a/clash_lib/src/proxy/converters/shadowsocks.rs b/clash_lib/src/proxy/converters/shadowsocks.rs index 28e448c3..5d6fe549 100644 --- a/clash_lib/src/proxy/converters/shadowsocks.rs +++ b/clash_lib/src/proxy/converters/shadowsocks.rs @@ -7,7 +7,7 @@ use crate::{ Handler, HandlerOptions, OBFSOption, ShadowTlsOption, SimpleOBFSMode, SimpleOBFSOption, V2RayOBFSOption, }, - CommonOption, + HandlerCommonOptions, }, Error, }; @@ -26,7 +26,7 @@ impl TryFrom<&OutboundShadowsocks> for Handler { fn try_from(s: &OutboundShadowsocks) -> Result { let h = Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/converters/socks5.rs b/clash_lib/src/proxy/converters/socks5.rs index fe1dd275..b9600bf4 100644 --- a/clash_lib/src/proxy/converters/socks5.rs +++ b/clash_lib/src/proxy/converters/socks5.rs @@ -2,7 +2,7 @@ use crate::{ config::internal::proxy::OutboundSocks5, proxy::{ socks::{Handler, HandlerOptions}, - CommonOption, + HandlerCommonOptions, }, }; @@ -20,7 +20,7 @@ impl TryFrom<&OutboundSocks5> for Handler { fn try_from(s: &OutboundSocks5) -> Result { let h = Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/converters/trojan.rs b/clash_lib/src/proxy/converters/trojan.rs index 11152340..6ff69156 100644 --- a/clash_lib/src/proxy/converters/trojan.rs +++ b/clash_lib/src/proxy/converters/trojan.rs @@ -5,7 +5,7 @@ use crate::{ proxy::{ options::{GrpcOption, WsOption}, trojan::{Handler, HandlerOptions, Transport}, - CommonOption, + HandlerCommonOptions, }, Error, }; @@ -32,7 +32,7 @@ impl TryFrom<&OutboundTrojan> for Handler { let h = Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/converters/tuic.rs b/clash_lib/src/proxy/converters/tuic.rs index cc06c2bb..c7015043 100644 --- a/clash_lib/src/proxy/converters/tuic.rs +++ b/clash_lib/src/proxy/converters/tuic.rs @@ -6,7 +6,7 @@ use crate::{ config::internal::proxy::OutboundTuic, proxy::{ tuic::{types::CongestionControl, Handler, HandlerOptions}, - CommonOption, + HandlerCommonOptions, }, }; @@ -25,7 +25,7 @@ impl TryFrom<&OutboundTuic> for Handler { Ok(Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), server: s.common_opts.server.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/converters/vmess.rs b/clash_lib/src/proxy/converters/vmess.rs index 4579060c..d133e320 100644 --- a/clash_lib/src/proxy/converters/vmess.rs +++ b/clash_lib/src/proxy/converters/vmess.rs @@ -6,7 +6,7 @@ use crate::{ options::{GrpcOption, Http2Option, WsOption}, transport::TLSOptions, vmess::{Handler, HandlerOptions, VmessTransport}, - CommonOption, + HandlerCommonOptions, }, Error, }; @@ -33,7 +33,7 @@ impl TryFrom<&OutboundVmess> for Handler { let h = Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/converters/wireguard.rs b/clash_lib/src/proxy/converters/wireguard.rs index c1589d10..bf1b4567 100644 --- a/clash_lib/src/proxy/converters/wireguard.rs +++ b/clash_lib/src/proxy/converters/wireguard.rs @@ -4,7 +4,7 @@ use crate::{ config::internal::proxy::OutboundWireguard, proxy::{ wg::{Handler, HandlerOptions}, - CommonOption, + HandlerCommonOptions, }, Error, }; @@ -23,7 +23,7 @@ impl TryFrom<&OutboundWireguard> for Handler { fn try_from(s: &OutboundWireguard) -> Result { let h = Handler::new(HandlerOptions { name: s.common_opts.name.to_owned(), - common_opts: CommonOption { + common_opts: HandlerCommonOptions { connector: s.common_opts.connect_via.clone(), ..Default::default() }, diff --git a/clash_lib/src/proxy/datagram.rs b/clash_lib/src/proxy/datagram.rs index f768ca68..f452aee5 100644 --- a/clash_lib/src/proxy/datagram.rs +++ b/clash_lib/src/proxy/datagram.rs @@ -1,20 +1,15 @@ use crate::{ - app::dns::ThreadSafeDNSResolver, - common::errors::new_io_error, - proxy::{socks::Socks5UDPCodec, AnyOutboundDatagram, InboundDatagram}, + app::dns::ThreadSafeDNSResolver, common::errors::new_io_error, session::SocksAddr, }; -use bytes::Bytes; -use futures::{ready, Sink, SinkExt, Stream, StreamExt}; +use futures::{ready, Sink, Stream}; use std::{ fmt::{Debug, Display, Formatter}, io, - net::SocketAddr, pin::Pin, task::{Context, Poll}, }; use tokio::{io::ReadBuf, net::UdpSocket}; -use tokio_util::udp::UdpFramed; #[derive(Clone)] pub struct UdpPacket { @@ -64,91 +59,9 @@ impl UdpPacket { } } -pub struct InboundUdp { - inner: I, -} - -impl InboundUdp -where - I: Stream + Unpin, - I: Sink<((Bytes, SocksAddr), SocketAddr)>, -{ - pub fn new(inner: I) -> Self { - Self { inner } - } -} - -impl Debug for InboundUdp> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InboundUdp").finish() - } -} - -impl Stream for InboundUdp> { - type Item = UdpPacket; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let pin = self.get_mut(); - - match pin.inner.poll_next_unpin(cx) { - Poll::Ready(item) => match item { - None => Poll::Ready(None), - Some(item) => match item { - Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket { - data: pkt.to_vec(), - src_addr: SocksAddr::Ip(src), - dst_addr: dst, - })), - Err(_) => Poll::Ready(None), - }, - }, - Poll::Pending => Poll::Pending, - } - } -} - -impl Sink for InboundUdp> { - type Error = std::io::Error; - - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let pin = self.get_mut(); - pin.inner.poll_ready_unpin(cx) - } - - fn start_send(self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { - let pin = self.get_mut(); - pin.inner.start_send_unpin(( - (item.data.into(), item.src_addr), - item.dst_addr.must_into_socket_addr(), - )) - } - - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let pin = self.get_mut(); - pin.inner.poll_flush_unpin(cx) - } - - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let pin = self.get_mut(); - pin.inner.poll_close_unpin(cx) - } -} - -impl InboundDatagram for InboundUdp> {} - #[must_use = "sinks do nothing unless polled"] +// TODO: maybe we should use abstract datagram IO interface instead of the +// Stream + Sink trait pub struct OutboundDatagramImpl { inner: UdpSocket, resolver: ThreadSafeDNSResolver, @@ -157,18 +70,13 @@ pub struct OutboundDatagramImpl { } impl OutboundDatagramImpl { - #[allow(clippy::new_ret_no_self)] - pub fn new( - udp: UdpSocket, - resolver: ThreadSafeDNSResolver, - ) -> AnyOutboundDatagram { - let s = Self { + pub fn new(udp: UdpSocket, resolver: ThreadSafeDNSResolver) -> Self { + Self { inner: udp, resolver, flushed: true, pkt: None, - }; - Box::new(s) as _ + } } } diff --git a/clash_lib/src/proxy/direct/mod.rs b/clash_lib/src/proxy/direct/mod.rs index f146cf1c..f3b9ed3f 100644 --- a/clash_lib/src/proxy/direct/mod.rs +++ b/clash_lib/src/proxy/direct/mod.rs @@ -74,7 +74,7 @@ impl OutboundHandler for Handler { (remote_ip, sess.destination.port()).into(), sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -92,7 +92,7 @@ impl OutboundHandler for Handler { None, sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await .map(|x| OutboundDatagramImpl::new(x, resolver))?; @@ -119,7 +119,7 @@ impl OutboundHandler for Handler { sess.destination.port(), sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; let s = ChainedStreamWrapper::new(s); @@ -140,7 +140,7 @@ impl OutboundHandler for Handler { sess.destination.clone(), sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; let d = ChainedDatagramWrapper::new(d); diff --git a/clash_lib/src/proxy/fallback/mod.rs b/clash_lib/src/proxy/group/fallback/mod.rs similarity index 92% rename from clash_lib/src/proxy/fallback/mod.rs rename to clash_lib/src/proxy/group/fallback/mod.rs index ac17d841..ebdd92fd 100644 --- a/clash_lib/src/proxy/fallback/mod.rs +++ b/clash_lib/src/proxy/group/fallback/mod.rs @@ -11,18 +11,17 @@ use crate::{ providers::proxy_provider::ThreadSafeProxyProvider, ProxyManager, }, }, + proxy::{ + utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, + AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, + }, session::Session, }; -use super::{ - utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, - AnyOutboundHandler, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, -}; - #[derive(Default, Clone)] pub struct HandlerOptions { - pub shared_opts: super::options::HandlerSharedOptions, + pub common_opts: HandlerCommonOptions, pub name: String, pub udp: bool, } @@ -150,6 +149,6 @@ impl OutboundHandler for Handler { } fn icon(&self) -> Option { - self.opts.shared_opts.icon.clone() + self.opts.common_opts.icon.clone() } } diff --git a/clash_lib/src/proxy/loadbalance/helpers.rs b/clash_lib/src/proxy/group/loadbalance/helpers.rs similarity index 100% rename from clash_lib/src/proxy/loadbalance/helpers.rs rename to clash_lib/src/proxy/group/loadbalance/helpers.rs diff --git a/clash_lib/src/proxy/loadbalance/mod.rs b/clash_lib/src/proxy/group/loadbalance/mod.rs similarity index 93% rename from clash_lib/src/proxy/loadbalance/mod.rs rename to clash_lib/src/proxy/group/loadbalance/mod.rs index 019d255f..1766d04d 100644 --- a/clash_lib/src/proxy/loadbalance/mod.rs +++ b/clash_lib/src/proxy/group/loadbalance/mod.rs @@ -13,20 +13,19 @@ use crate::{ remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, config::internal::proxy::LoadBalanceStrategy, + proxy::{ + utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, + AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, + }, session::Session, }; use self::helpers::{strategy_consistent_hashring, strategy_rr, StrategyFn}; -use super::{ - utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, - AnyOutboundHandler, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, -}; - #[derive(Default, Clone)] pub struct HandlerOptions { - pub shared_opts: super::options::HandlerSharedOptions, + pub common_opts: HandlerCommonOptions, pub name: String, pub udp: bool, pub strategy: LoadBalanceStrategy, @@ -157,6 +156,6 @@ impl OutboundHandler for Handler { } fn icon(&self) -> Option { - self.opts.shared_opts.icon.clone() + self.opts.common_opts.icon.clone() } } diff --git a/clash_lib/src/proxy/group/mod.rs b/clash_lib/src/proxy/group/mod.rs new file mode 100644 index 00000000..12c632b7 --- /dev/null +++ b/clash_lib/src/proxy/group/mod.rs @@ -0,0 +1,5 @@ +pub mod fallback; +pub mod loadbalance; +pub mod relay; +pub mod selector; +pub mod urltest; diff --git a/clash_lib/src/proxy/relay/mod.rs b/clash_lib/src/proxy/group/relay/mod.rs similarity index 92% rename from clash_lib/src/proxy/relay/mod.rs rename to clash_lib/src/proxy/group/relay/mod.rs index 7ae59ae8..ac781b9f 100644 --- a/clash_lib/src/proxy/relay/mod.rs +++ b/clash_lib/src/proxy/group/relay/mod.rs @@ -15,21 +15,20 @@ use crate::{ remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, common::errors::new_io_error, - session::Session, -}; - -use super::{ - utils::{ - provider_helper::get_proxies_from_providers, DirectConnector, - ProxyConnector, RemoteConnector, + proxy::{ + utils::{ + provider_helper::get_proxies_from_providers, DirectConnector, + ProxyConnector, RemoteConnector, + }, + AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, }, - AnyOutboundHandler, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, + session::Session, }; #[derive(Default)] pub struct HandlerOptions { - pub shared_opts: super::options::HandlerSharedOptions, + pub common_opts: HandlerCommonOptions, pub name: String, } @@ -194,12 +193,12 @@ impl OutboundHandler for Handler { } fn icon(&self) -> Option { - self.opts.shared_opts.icon.clone() + self.opts.common_opts.icon.clone() } } #[cfg(feature = "shadowsocks")] -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use tokio::sync::RwLock; @@ -250,11 +249,9 @@ mod tests { provider.expect_touch().returning(|| ()); provider.expect_healthcheck().returning(|| ()); - provider.expect_proxies().returning(move || { - let mut proxies = Vec::new(); - proxies.push(ss_handler.clone()); - proxies - }); + provider + .expect_proxies() + .returning(move || vec![ss_handler.clone()]); let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); @@ -288,12 +285,9 @@ mod tests { provider.expect_touch().returning(|| ()); provider.expect_healthcheck().returning(|| ()); - provider.expect_proxies().returning(move || { - let mut proxies = Vec::new(); - proxies.push(ss_handler.clone()); - proxies.push(ss_handler.clone()); - proxies - }); + provider + .expect_proxies() + .returning(move || vec![ss_handler.clone(), ss_handler.clone()]); let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]); diff --git a/clash_lib/src/proxy/selector/mod.rs b/clash_lib/src/proxy/group/selector/mod.rs similarity index 95% rename from clash_lib/src/proxy/selector/mod.rs rename to clash_lib/src/proxy/group/selector/mod.rs index 5214945e..63454b89 100644 --- a/clash_lib/src/proxy/selector/mod.rs +++ b/clash_lib/src/proxy/group/selector/mod.rs @@ -11,16 +11,15 @@ use crate::{ dns::ThreadSafeDNSResolver, remote_content_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, + proxy::{ + utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, + AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, + }, session::Session, Error, }; -use super::{ - utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, - AnyOutboundHandler, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, -}; - #[async_trait] pub trait SelectorControl { async fn select(&mut self, name: &str) -> Result<(), Error>; @@ -35,7 +34,7 @@ struct HandlerInner { #[derive(Default, Clone)] pub struct HandlerOptions { - pub shared_opts: super::options::HandlerSharedOptions, + pub common_opts: HandlerCommonOptions, pub name: String, pub udp: bool, } @@ -202,7 +201,7 @@ impl OutboundHandler for Handler { } fn icon(&self) -> Option { - self.opts.shared_opts.icon.clone() + self.opts.common_opts.icon.clone() } } @@ -213,8 +212,8 @@ mod tests { use tokio::sync::{Mutex, RwLock}; use crate::proxy::{ + group::selector::ThreadSafeSelectorControl, mocks::{MockDummyOutboundHandler, MockDummyProxyProvider}, - selector::ThreadSafeSelectorControl, }; #[tokio::test] diff --git a/clash_lib/src/proxy/urltest/mod.rs b/clash_lib/src/proxy/group/urltest/mod.rs similarity index 94% rename from clash_lib/src/proxy/urltest/mod.rs rename to clash_lib/src/proxy/group/urltest/mod.rs index c8bae835..f2e2d2c2 100644 --- a/clash_lib/src/proxy/urltest/mod.rs +++ b/clash_lib/src/proxy/group/urltest/mod.rs @@ -13,18 +13,17 @@ use crate::{ providers::proxy_provider::ThreadSafeProxyProvider, ProxyManager, }, }, + proxy::{ + utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, + AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, + }, session::Session, }; -use super::{ - utils::{provider_helper::get_proxies_from_providers, RemoteConnector}, - AnyOutboundHandler, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, -}; - #[derive(Default)] pub struct HandlerOptions { - pub shared_opts: super::options::HandlerSharedOptions, + pub common_opts: HandlerCommonOptions, pub name: String, pub udp: bool, } @@ -121,11 +120,11 @@ impl Handler { fastest_delay ); - return inner + inner .fastest_proxy .as_ref() .unwrap_or(proxies.first().unwrap()) - .clone(); + .clone() } } @@ -228,6 +227,6 @@ impl OutboundHandler for Handler { } fn icon(&self) -> Option { - self.opts.shared_opts.icon.clone() + self.opts.common_opts.icon.clone() } } diff --git a/clash_lib/src/proxy/http/inbound/mod.rs b/clash_lib/src/proxy/http/inbound/mod.rs index 8c8cc5dc..3f73d15a 100644 --- a/clash_lib/src/proxy/http/inbound/mod.rs +++ b/clash_lib/src/proxy/http/inbound/mod.rs @@ -4,7 +4,7 @@ mod proxy; use crate::{ common::auth::ThreadSafeAuthenticator, - proxy::{utils::apply_tcp_options, AnyInboundListener, InboundListener}, + proxy::{utils::apply_tcp_options, InboundListener}, Dispatcher, }; use async_trait::async_trait; @@ -29,17 +29,16 @@ impl Drop for Listener { } impl Listener { - #[allow(clippy::new_ret_no_self)] pub fn new( addr: SocketAddr, dispatcher: Arc, authenticator: ThreadSafeAuthenticator, - ) -> AnyInboundListener { - Arc::new(Self { + ) -> Self { + Self { addr, dispatcher, authenticator, - }) as _ + } } } diff --git a/clash_lib/src/proxy/mixed/mod.rs b/clash_lib/src/proxy/mixed/mod.rs index ce5ae863..4fb9ac1b 100644 --- a/clash_lib/src/proxy/mixed/mod.rs +++ b/clash_lib/src/proxy/mixed/mod.rs @@ -1,6 +1,6 @@ use crate::{ common::auth::ThreadSafeAuthenticator, - proxy::{AnyInboundListener, InboundListener}, + proxy::InboundListener, session::{Network, Session}, Dispatcher, }; @@ -25,17 +25,16 @@ impl Drop for Listener { } impl Listener { - #[allow(clippy::new_ret_no_self)] pub fn new( addr: SocketAddr, dispatcher: Arc, authenticator: ThreadSafeAuthenticator, - ) -> AnyInboundListener { - Arc::new(Self { + ) -> Self { + Self { addr, dispatcher, authenticator, - }) as _ + } } } diff --git a/clash_lib/src/proxy/mod.rs b/clash_lib/src/proxy/mod.rs index 021f0476..4a02e877 100644 --- a/clash_lib/src/proxy/mod.rs +++ b/clash_lib/src/proxy/mod.rs @@ -3,7 +3,7 @@ use crate::{ dispatcher::{BoxedChainedDatagram, BoxedChainedStream}, dns::ThreadSafeDNSResolver, }, - proxy::{datagram::UdpPacket, utils::Interface}, + proxy::datagram::UdpPacket, session::Session, }; use async_trait::async_trait; @@ -28,6 +28,8 @@ pub mod reject; pub mod http; pub mod mixed; +#[cfg(target_os = "linux")] +pub mod tproxy; pub(crate) mod datagram; @@ -36,6 +38,7 @@ pub mod hysteria2; #[cfg(feature = "shadowsocks")] pub mod shadowsocks; pub mod socks; +#[cfg(feature = "onion")] pub mod tor; pub mod trojan; #[cfg(feature = "tuic")] @@ -45,17 +48,15 @@ pub mod utils; pub mod vmess; pub mod wg; -pub mod fallback; -pub mod loadbalance; -pub mod relay; -pub mod selector; -pub mod urltest; +pub mod group; +pub use group::{fallback, loadbalance, relay, selector, urltest}; mod common; mod options; -pub use options::HandlerSharedOptions; mod transport; +pub use options::HandlerCommonOptions; + #[cfg(test)] pub mod mocks; @@ -83,6 +84,10 @@ pub trait InboundDatagram: Stream + Sink + Send + Sync + Unpin + Debug { } +impl InboundDatagram for T where + T: Stream + Sink + Send + Sync + Unpin + Debug +{ +} pub type AnyInboundDatagram = Box>; @@ -99,14 +104,6 @@ impl OutboundDatagram for T where pub type AnyOutboundDatagram = Box>; -#[derive(Default, Debug, Clone)] -pub struct CommonOption { - #[allow(dead_code)] - so_mark: Option, - iface: Option, - connector: Option, -} - #[async_trait] pub trait InboundListener: Send + Sync + Unpin { /// support tcp or not diff --git a/clash_lib/src/proxy/options.rs b/clash_lib/src/proxy/options.rs index a16d2149..525d023f 100644 --- a/clash_lib/src/proxy/options.rs +++ b/clash_lib/src/proxy/options.rs @@ -24,8 +24,8 @@ pub struct WsOption { pub early_data_header_name: String, } -// TODO: merge this with CommonOptions -#[derive(Clone, Default)] -pub struct HandlerSharedOptions { +#[derive(Default, Debug, Clone)] +pub struct HandlerCommonOptions { + pub connector: Option, pub icon: Option, } diff --git a/clash_lib/src/proxy/shadowsocks/datagram.rs b/clash_lib/src/proxy/shadowsocks/datagram.rs index b20e392b..19b38140 100644 --- a/clash_lib/src/proxy/shadowsocks/datagram.rs +++ b/clash_lib/src/proxy/shadowsocks/datagram.rs @@ -1,51 +1,55 @@ use std::{ io, + net::SocketAddr, pin::Pin, task::{Context, Poll}, }; -use futures::{ready, Sink, SinkExt, Stream, StreamExt}; -use shadowsocks::ProxySocket; +use bytes::BytesMut; +use futures::{ + ready, + stream::{SplitSink, SplitStream}, + Sink, SinkExt, Stream, StreamExt, +}; +use shadowsocks::{ + relay::udprelay::{DatagramReceive, DatagramSend}, + ProxySocket, +}; use tokio::io::ReadBuf; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; use crate::{ - app::dns::ThreadSafeDNSResolver, common::errors::new_io_error, proxy::{datagram::UdpPacket, AnyOutboundDatagram}, session::SocksAddr, }; -/// the outbound datagram for that shadowsocks returns to us -pub struct OutboundDatagramShadowsocks { - inner: ProxySocket, - remote_addr: SocksAddr, +/// OutboundDatagram wrapper for shadowsocks socket, that takes ShadowsocksUdpIo +/// as underlying I/O +pub struct OutboundDatagramShadowsocks { + inner: ProxySocket, + remote_addr: SocketAddr, flushed: bool, pkt: Option, buf: Vec, - resolver: ThreadSafeDNSResolver, } -impl OutboundDatagramShadowsocks { - #[allow(clippy::new_ret_no_self)] - pub fn new( - inner: ProxySocket, - remote_addr: (String, u16), - resolver: ThreadSafeDNSResolver, - ) -> AnyOutboundDatagram { - let s = Self { +impl OutboundDatagramShadowsocks { + pub fn new(inner: ProxySocket, remote_addr: SocketAddr) -> Self { + Self { inner, flushed: true, pkt: None, - remote_addr: remote_addr.try_into().expect("must into socks addr"), + remote_addr, buf: vec![0u8; 65535], - resolver, - }; - Box::new(s) as _ + } } } -impl Sink for OutboundDatagramShadowsocks { +impl Sink for OutboundDatagramShadowsocks +where + S: DatagramSend + Unpin, +{ type Error = io::Error; fn poll_ready( @@ -83,33 +87,9 @@ impl Sink for OutboundDatagramShadowsocks { ref mut pkt, ref remote_addr, ref mut flushed, - ref mut resolver, .. } = *self; - let dst = match remote_addr.to_owned() { - SocksAddr::Domain(domain, port) => { - let domain = domain.to_string(); - let port = port.to_owned(); - - let mut fut = resolver.resolve(domain.as_str(), false); - let ip = ready!(fut.as_mut().poll(cx).map_err(|_| { - io::Error::new(io::ErrorKind::Other, "resolve domain failed") - }))?; - - if let Some(ip) = ip { - trace!("resolve domain {} to {}", domain, ip); - (ip, port).into() - } else { - return Poll::Ready(Err(io::Error::new( - io::ErrorKind::Other, - format!("resolve domain failed: {}", domain), - ))); - } - } - SocksAddr::Ip(addr) => addr, - }; - let pkt_container = pkt; if let Some(pkt) = pkt_container { @@ -117,12 +97,12 @@ impl Sink for OutboundDatagramShadowsocks { let addr: shadowsocks::relay::Address = (pkt.dst_addr.host(), pkt.dst_addr.port()).into(); - let n = ready!(inner.poll_send_to(dst, &addr, data, cx))?; + let n = ready!(inner.poll_send_to(*remote_addr, &addr, data, cx))?; debug!( "send udp packet to remote ss server, len: {}, remote_addr: {}, \ dst_addr: {}", - n, dst, addr + n, remote_addr, addr ); let wrote_all = n == data.len(); @@ -156,29 +136,35 @@ impl Sink for OutboundDatagramShadowsocks { } } -impl Stream for OutboundDatagramShadowsocks { +impl Stream for OutboundDatagramShadowsocks +where + S: DatagramReceive + Unpin, +{ type Item = UdpPacket; #[instrument(skip(self, cx))] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { let Self { ref mut buf, ref inner, .. - } = *self; + } = self.get_mut(); let mut buf = ReadBuf::new(buf); - let rv = ready!(inner.poll_recv_from(cx, &mut buf)); + let rv = ready!(inner.poll_recv(cx, &mut buf)); debug!("recv udp packet from remote ss server: {:?}", rv); match rv { Ok((n, src, ..)) => Poll::Ready(Some(UdpPacket { data: buf.filled()[..n].to_vec(), - src_addr: src.into(), + src_addr: match src { + shadowsocks::relay::Address::SocketAddress(a) => a.into(), + _ => SocksAddr::any_ipv4(), + }, dst_addr: SocksAddr::any_ipv4(), })), Err(_) => Poll::Ready(None), @@ -186,77 +172,96 @@ impl Stream for OutboundDatagramShadowsocks { } } -/// Shadowsocks UDP I/O that is passed to shadowsocks relay +/// Shadowsocks UDP I/O that ProxySocket required pub(crate) struct ShadowsocksUdpIo { - inner: AnyOutboundDatagram, + w: tokio::sync::Mutex>, + r: tokio::sync::Mutex<(SplitStream, BytesMut)>, } impl ShadowsocksUdpIo { pub fn new(inner: AnyOutboundDatagram) -> Self { - Self { inner } + let (w, r) = inner.split(); + Self { + w: tokio::sync::Mutex::new(w), + r: tokio::sync::Mutex::new((r, BytesMut::new())), + } } } -impl Sink - for ShadowsocksUdpIo -{ - type Error = io::Error; - - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.inner.poll_ready_unpin(cx) - } - - fn start_send( - mut self: Pin<&mut Self>, - item: shadowsocks::relay::udprelay::proxy_socket::UdpPacket, - ) -> Result<(), Self::Error> { - self.inner.start_send_unpin(UdpPacket { - data: item.data.to_vec(), - src_addr: item.src.map(|x| x.into()).unwrap_or_default(), - dst_addr: item.dst.map(|x| x.into()).unwrap_or_default(), - }) +impl DatagramSend for ShadowsocksUdpIo { + fn poll_send(&self, _: &mut Context<'_>, _: &[u8]) -> Poll> { + Poll::Ready(Err(new_io_error("not supported for shadowsocks udp io"))) } - fn poll_flush( - mut self: Pin<&mut Self>, + fn poll_send_to( + &self, cx: &mut Context<'_>, - ) -> Poll> { - self.inner.poll_flush_unpin(cx) + buf: &[u8], + target: std::net::SocketAddr, + ) -> Poll> { + let mut w = self.w.try_lock().expect("must acquire"); + match w.start_send_unpin(UdpPacket { + data: buf.to_vec(), + src_addr: SocksAddr::any_ipv4(), + dst_addr: target.into(), + }) { + Ok(_) => {} + Err(e) => return Poll::Ready(Err(new_io_error(e.to_string()))), + } + match w.poll_flush_unpin(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(buf.len())), + Poll::Ready(Err(e)) => Poll::Ready(Err(new_io_error(e.to_string()))), + Poll::Pending => Poll::Pending, + } } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.inner.poll_close_unpin(cx) + fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll> { + let mut w = self.w.try_lock().expect("must acquire"); + w.poll_ready_unpin(cx) + .map_err(|e| new_io_error(e.to_string())) } } -impl Stream for ShadowsocksUdpIo { - type Item = shadowsocks::relay::udprelay::proxy_socket::UdpPacket; - - fn poll_next( - mut self: Pin<&mut Self>, +impl DatagramReceive for ShadowsocksUdpIo { + fn poll_recv( + &self, cx: &mut Context<'_>, - ) -> Poll> { - match ready!(self.inner.poll_next_unpin(cx)) { - Some(pkt) => { - let (src, dst) = ( - pkt.src_addr.must_into_socket_addr(), - pkt.dst_addr.must_into_socket_addr(), - ); - Poll::Ready(Some( - shadowsocks::relay::udprelay::proxy_socket::UdpPacket { - data: pkt.data.into(), - src: src.into(), - dst: dst.into(), - }, - )) + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut g = self.r.try_lock().expect("must acquire"); + let (r, remained) = &mut *g; + + if !remained.is_empty() { + let to_consume = buf.remaining().min(remained.len()); + let consume = remained.split_to(to_consume); + buf.put_slice(&consume); + Poll::Ready(Ok(())) + } else { + match r.poll_next_unpin(cx) { + Poll::Ready(Some(pkt)) => { + let to_comsume = buf.remaining().min(pkt.data.len()); + let consume = pkt.data[..to_comsume].to_vec(); + buf.put_slice(&consume); + if to_comsume < pkt.data.len() { + remained.extend_from_slice(&pkt.data[to_comsume..]); + } + Poll::Ready(Ok(())) + } + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(Ok(())), } - None => Poll::Ready(None), } } + + fn poll_recv_from( + &self, + _: &mut Context<'_>, + _: &mut ReadBuf<'_>, + ) -> Poll> { + Poll::Ready(Err(new_io_error("not supported for shadowsocks udp io"))) + } + + fn poll_recv_ready(&self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } } diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index 41c4e4a3..a7a8dd02 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -6,7 +6,7 @@ mod v2ray; use self::{datagram::OutboundDatagramShadowsocks, stream::ShadowSocksStream}; use super::{ - utils::{new_udp_socket, RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, + utils::{RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, AnyStream, ConnectorType, DialWithConnector, OutboundType, }; use crate::{ @@ -17,8 +17,9 @@ use crate::{ }, dns::ThreadSafeDNSResolver, }, + common::errors::new_io_error, impl_default_connector, - proxy::{CommonOption, OutboundHandler}, + proxy::{HandlerCommonOptions, OutboundHandler}, session::Session, }; use async_trait::async_trait; @@ -68,7 +69,7 @@ pub enum OBFSOption { pub struct HandlerOptions { pub name: String, - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, pub server: String, pub port: u16, pub password: String, @@ -204,30 +205,24 @@ impl OutboundHandler for Handler { async fn connect_datagram( &self, - #[allow(unused_variables)] sess: &Session, + sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { - let ctx = Context::new_shared(ServerType::Local); - let cfg = self.server_config()?; + let dialer = self.connector.lock().await; - let socket = new_udp_socket( - None, - self.opts.common_opts.iface.clone().or(sess.iface.clone()), - #[cfg(any(target_os = "linux", target_os = "android"))] - None, - ) - .await?; + if let Some(dialer) = dialer.as_ref() { + debug!("{:?} is connecting via {:?}", self, dialer); + } - let socket: ProxySocket = - ProxySocket::from_socket(UdpSocketType::Client, ctx, &cfg, socket); - let d = OutboundDatagramShadowsocks::new( - socket, - (self.opts.server.to_owned(), self.opts.port), + self.connect_datagram_with_connector( + sess, resolver, - ); - let d = ChainedDatagramWrapper::new(d); - d.append_to_chain(self.name()).await; - Ok(Box::new(d)) + dialer + .as_ref() + .unwrap_or(&GLOBAL_DIRECT_CONNECTOR.clone()) + .as_ref(), + ) + .await } async fn support_connector(&self) -> ConnectorType { @@ -245,9 +240,9 @@ impl OutboundHandler for Handler { resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -271,30 +266,34 @@ impl OutboundHandler for Handler { resolver.clone(), None, (self.opts.server.clone(), self.opts.port).try_into()?, - self.opts - .common_opts - .iface - .as_ref() - .or(sess.iface.as_ref()) - .cloned(), + sess.iface.as_ref().cloned(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; - let socket = ProxySocket::from_io( + let socket = ProxySocket::from_socket( UdpSocketType::Client, ctx, &cfg, - Box::new(ShadowsocksUdpIo::new(socket)), - None, - #[cfg(unix)] - None, + ShadowsocksUdpIo::new(socket), ); + let server_addr = resolver + .resolve(&self.opts.server, false) + .await + .map_err(|x| { + new_io_error(format!( + "failed to resolve {}: {}", + self.opts.server, x + )) + })? + .ok_or(new_io_error(format!( + "failed to resolve {}", + self.opts.server + )))?; let d = OutboundDatagramShadowsocks::new( socket, - (self.opts.server.to_owned(), self.opts.port), - resolver, + (server_addr, self.opts.port).into(), ); let d = ChainedDatagramWrapper::new(d); d.append_to_chain(self.name()).await; @@ -302,15 +301,18 @@ impl OutboundHandler for Handler { } } -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use super::super::utils::test_utils::{ consts::*, docker_runner::DockerTestRunner, }; - use crate::proxy::utils::test_utils::{ - docker_runner::{DockerTestRunnerBuilder, MultiDockerTestRunner}, - run_test_suites_and_cleanup, Suite, + use crate::{ + proxy::utils::test_utils::{ + docker_runner::{DockerTestRunnerBuilder, MultiDockerTestRunner}, + run_test_suites_and_cleanup, Suite, + }, + tests::initialize, }; use super::*; @@ -331,8 +333,8 @@ mod tests { #[tokio::test] #[serial_test::serial] - async fn test_ss() -> anyhow::Result<()> { - let _ = tracing_subscriber::fmt().try_init(); + async fn test_ss_plain() -> anyhow::Result<()> { + initialize(); let opts = HandlerOptions { name: "test-ss".to_owned(), common_opts: Default::default(), @@ -480,9 +482,6 @@ mod tests { #[serial_test::serial] async fn test_ss_obfs_tls() -> anyhow::Result<()> { if cfg!(target_arch = "x86_64") { - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); test_ss_obfs_inner(SimpleOBFSMode::Tls).await } else { eprintln!("test_ss_obfs_tls is ignored on non-x86_64 platform"); diff --git a/clash_lib/src/proxy/socks/inbound/datagram.rs b/clash_lib/src/proxy/socks/inbound/datagram.rs index 1e45427e..e8f80a07 100644 --- a/clash_lib/src/proxy/socks/inbound/datagram.rs +++ b/clash_lib/src/proxy/socks/inbound/datagram.rs @@ -1,7 +1,16 @@ -use crate::session::SocksAddr; +use crate::{proxy::datagram::UdpPacket, session::SocksAddr}; use bytes::{Buf, BufMut, Bytes, BytesMut}; -use std::io; -use tokio_util::codec::{Decoder, Encoder}; +use futures::{Sink, SinkExt, Stream, StreamExt}; +use std::{ + io, + net::SocketAddr, + pin::Pin, + task::{Context, Poll}, +}; +use tokio_util::{ + codec::{Decoder, Encoder}, + udp::UdpFramed, +}; // +----+------+------+----------+----------+----------+ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | @@ -65,3 +74,85 @@ impl Decoder for Socks5UDPCodec { Ok(Some((addr, packet))) } } + +pub struct InboundUdp { + inner: I, +} + +impl InboundUdp +where + I: Stream + Unpin, + I: Sink<((Bytes, SocksAddr), SocketAddr)>, +{ + pub fn new(inner: I) -> Self { + Self { inner } + } +} + +impl std::fmt::Debug for InboundUdp> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InboundUdp").finish() + } +} + +impl Stream for InboundUdp> { + type Item = UdpPacket; + + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let pin = self.get_mut(); + + match pin.inner.poll_next_unpin(cx) { + Poll::Ready(item) => match item { + None => Poll::Ready(None), + Some(item) => match item { + Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket { + data: pkt.to_vec(), + src_addr: SocksAddr::Ip(src), + dst_addr: dst, + })), + Err(_) => Poll::Ready(None), + }, + }, + Poll::Pending => Poll::Pending, + } + } +} + +impl Sink for InboundUdp> { + type Error = std::io::Error; + + fn poll_ready( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let pin = self.get_mut(); + pin.inner.poll_ready_unpin(cx) + } + + fn start_send(self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> { + let pin = self.get_mut(); + pin.inner.start_send_unpin(( + (item.data.into(), item.src_addr), + item.dst_addr.must_into_socket_addr(), + )) + } + + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let pin = self.get_mut(); + pin.inner.poll_flush_unpin(cx) + } + + fn poll_close( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let pin = self.get_mut(); + pin.inner.poll_close_unpin(cx) + } +} diff --git a/clash_lib/src/proxy/socks/inbound/mod.rs b/clash_lib/src/proxy/socks/inbound/mod.rs index ddfa682c..4062eb7a 100644 --- a/clash_lib/src/proxy/socks/inbound/mod.rs +++ b/clash_lib/src/proxy/socks/inbound/mod.rs @@ -3,7 +3,7 @@ mod stream; use crate::{ common::auth::ThreadSafeAuthenticator, - proxy::{utils::apply_tcp_options, AnyInboundListener, InboundListener}, + proxy::{utils::apply_tcp_options, InboundListener}, session::{Network, Session, Type}, Dispatcher, }; @@ -28,17 +28,16 @@ impl Drop for Listener { } impl Listener { - #[allow(clippy::new_ret_no_self)] pub fn new( addr: SocketAddr, dispatcher: Arc, authenticator: ThreadSafeAuthenticator, - ) -> AnyInboundListener { - Arc::new(Self { + ) -> Self { + Self { addr, dispatcher, authenticator, - }) as _ + } } } diff --git a/clash_lib/src/proxy/socks/inbound/stream.rs b/clash_lib/src/proxy/socks/inbound/stream.rs index 4a7de423..2294fe04 100644 --- a/clash_lib/src/proxy/socks/inbound/stream.rs +++ b/clash_lib/src/proxy/socks/inbound/stream.rs @@ -1,8 +1,8 @@ use crate::{ common::{auth::ThreadSafeAuthenticator, errors::new_io_error}, proxy::{ - datagram::InboundUdp, socks::{ + inbound::datagram::InboundUdp, socks5::{auth_methods, response_code, socks_command}, Socks5UDPCodec, SOCKS5_VERSION, }, @@ -178,7 +178,7 @@ pub async fn handle_tcp<'a>( let sess = Session { network: Network::Udp, typ: Type::Socks5, - packet_mark: None, + so_mark: None, iface: None, ..Default::default() }; diff --git a/clash_lib/src/proxy/socks/outbound/mod.rs b/clash_lib/src/proxy/socks/outbound/mod.rs index f0b2497f..75877db1 100644 --- a/clash_lib/src/proxy/socks/outbound/mod.rs +++ b/clash_lib/src/proxy/socks/outbound/mod.rs @@ -15,8 +15,8 @@ use crate::{ proxy::{ transport::{self, TLSOptions}, utils::{new_udp_socket, RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, - AnyStream, CommonOption, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, + AnyStream, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, }, session::Session, }; @@ -30,7 +30,7 @@ use super::socks5::{client_handshake, socks_command}; #[derive(Default)] pub struct HandlerOptions { pub name: String, - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, pub server: String, pub port: u16, pub user: Option, @@ -149,7 +149,7 @@ impl Handler { let udp_socket = new_udp_socket( None, - self.opts.common_opts.iface.clone().or(sess.iface.clone()), + sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] None, ) @@ -238,9 +238,9 @@ impl OutboundHandler for Handler { resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -262,9 +262,9 @@ impl OutboundHandler for Handler { resolver.clone(), self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -276,7 +276,7 @@ impl OutboundHandler for Handler { } } -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use std::sync::Arc; @@ -326,7 +326,6 @@ mod tests { #[tokio::test] #[serial_test::serial] async fn test_socks5_no_auth() -> anyhow::Result<()> { - let _ = tracing_subscriber::fmt().try_init(); let opts = HandlerOptions { name: "test-socks5-no-auth".to_owned(), common_opts: Default::default(), @@ -352,7 +351,6 @@ mod tests { async fn test_socks5_auth() -> anyhow::Result<()> { use crate::proxy::DialWithConnector; - let _ = tracing_subscriber::fmt().try_init(); let opts = HandlerOptions { name: "test-socks5-no-auth".to_owned(), common_opts: Default::default(), diff --git a/clash_lib/src/proxy/tproxy/iptables.sh b/clash_lib/src/proxy/tproxy/iptables.sh new file mode 100755 index 00000000..61b9e2ff --- /dev/null +++ b/clash_lib/src/proxy/tproxy/iptables.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +# ip to bypass tproxy +readonly LOCAL_BY_PASS="\ +127/8 \ +10/8 \ +" + +# declare ip as local for tproxy +ip rule del fwmark 0x3333 lookup 3333 +ip rule add fwmark 0x3333 lookup 3333 +ip route del local 0.0.0.0/0 dev lo table 3333 +ip route add local 0.0.0.0/0 dev lo table 3333 + +# where all traffic enter tproxy and get marked +iptables -t mangle -N CLASH-TPROXY-INPUT +# fill in the chain +for i in $LOCAL_BY_PASS; do + iptables -t mangle -A CLASH-TPROXY-INPUT -d $i -j RETURN +done +iptables -t mangle -A CLASH-TPROXY-INPUT -p tcp -j TPROXY \ + --tproxy-mark 0x3333/0x3333 --on-port 8900 --on-ip 127.0.0.1 +iptables -t mangle -A CLASH-TPROXY-INPUT -p udp -j TPROXY \ + --tproxy-mark 0x3333/0x3333 --on-port 8900 --on-ip 127.0.0.1 + +# for local traffic +iptables -t mangle -N CLASH-TPROXY-LOCAL +for i in $LOCAL_BY_PASS; do + iptables -t mangle -A CLASH-TPROXY-LOCAL -d $i -j RETURN +done +iptables -t mangle -A CLASH-TPROXY-LOCAL -p tcp -m conntrack --ctdir REPLY -j RETURN +iptables -t mangle -A CLASH-TPROXY-LOCAL -p udp -m conntrack --ctdir REPLY -j RETURN + +iptables -t mangle -A CLASH-TPROXY-LOCAL -m owner --uid-owner root -j RETURN +# https://github.com/shadowsocks/shadowsocks-rust/blob/6e6e6948d7fc426c99cc03ef91abae989b6482b4/configs/iptables_tproxy.sh#L187 +iptables -t mangle -A CLASH-TPROXY-LOCAL -p tcp -j MARK --set-xmark 0x3333/0xffffffff # needs to match the ip rule fwmark +iptables -t mangle -A CLASH-TPROXY-LOCAL -p udp -j MARK --set-xmark 0x3333/0xffffffff + +iptables -t mangle -A OUTPUT -p tcp -d 104.21.58.154 -j CLASH-TPROXY-LOCAL +iptables -t mangle -A OUTPUT -p tcp -d 172.67.161.121 -j CLASH-TPROXY-LOCAL +iptables -t mangle -A OUTPUT -p udp -d 1.1.1.1 -j CLASH-TPROXY-LOCAL +iptables -t mangle -A OUTPUT -p udp -d 8.8.8.8 -j CLASH-TPROXY-LOCAL + +# for routed traffic +iptables -t mangle -A PREROUTING -p tcp -j CLASH-TPROXY-INPUT +iptables -t mangle -A PREROUTING -p udp -j CLASH-TPROXY-INPUT + + + +# ipv6 +# TODO \ No newline at end of file diff --git a/clash_lib/src/proxy/tproxy/mod.rs b/clash_lib/src/proxy/tproxy/mod.rs new file mode 100644 index 00000000..083c33f5 --- /dev/null +++ b/clash_lib/src/proxy/tproxy/mod.rs @@ -0,0 +1,188 @@ +use super::tun::TunDatagram; +use crate::{ + app::dispatcher::Dispatcher, + proxy::{datagram::UdpPacket, utils::apply_tcp_options, InboundListener}, + session::{Network, Session, Type}, +}; +use async_trait::async_trait; +use socket2::{Domain, Socket}; +use std::{ + net::SocketAddr, + os::fd::{AsFd, AsRawFd}, + sync::Arc, +}; +use tokio::net::TcpListener; +use tracing::{trace, warn}; + +pub struct Listener { + addr: SocketAddr, + dispather: Arc, +} + +impl Drop for Listener { + fn drop(&mut self) { + warn!("Tproxy inbound listener on {} stopped", self.addr); + } +} + +impl Listener { + pub fn new(addr: SocketAddr, dispather: Arc) -> Self { + Self { addr, dispather } + } +} + +#[async_trait] +impl InboundListener for Listener { + fn handle_tcp(&self) -> bool { + true + } + + fn handle_udp(&self) -> bool { + true + } + + async fn listen_tcp(&self) -> std::io::Result<()> { + let socket = + Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?; + socket.set_ip_transparent(true)?; + socket.set_nonblocking(true)?; + socket.bind(&self.addr.into())?; + socket.listen(1024)?; + + let listener = TcpListener::from_std(socket.into())?; + + loop { + let (socket, src_addr) = listener.accept().await?; + + let socket = apply_tcp_options(socket)?; + + // local_addr is getsockname + let orig_dst = socket.local_addr()?; + + let sess = Session { + network: Network::Tcp, + typ: Type::Tproxy, + source: src_addr, + destination: orig_dst.into(), + ..Default::default() + }; + + trace!("tproxy new tcp conn {}", sess); + + let dispatcher = self.dispather.clone(); + tokio::spawn(async move { + dispatcher.dispatch_stream(sess, socket).await; + }); + } + } + + async fn listen_udp(&self) -> std::io::Result<()> { + let socket = Socket::new(Domain::IPV4, socket2::Type::DGRAM, None)?; + socket.set_ip_transparent(true)?; + socket.set_nonblocking(true)?; + socket.set_broadcast(true)?; + + let enable = 1u32; + let payload = std::ptr::addr_of!(enable).cast(); + unsafe { + libc::setsockopt( + socket.as_fd().as_raw_fd(), + libc::IPPROTO_IP, + libc::IP_RECVORIGDSTADDR, + payload, + std::mem::size_of_val(&enable) as libc::socklen_t, + ) + }; + socket.bind(&self.addr.into())?; + + let listener = unix_udp_sock::UdpSocket::from_std(socket.into())?; + + handle_inbound_datagram(Arc::new(listener), self.dispather.clone()).await + } +} + +async fn handle_inbound_datagram( + socket: Arc, + dispatcher: Arc, +) -> std::io::Result<()> { + // dispatcher <-> tproxy communications + let (l_tx, mut l_rx) = tokio::sync::mpsc::channel(32); + + // forward packets from tproxy to dispatcher + let (d_tx, d_rx) = tokio::sync::mpsc::channel(32); + + // for dispatcher - the dispatcher would receive packets from this channel, + // which is from the stack and send back packets to this channel, which is + // to the tproxy + let udp_stream = TunDatagram::new(l_tx, d_rx); + + let sess = Session { + network: Network::Udp, + typ: Type::Tproxy, + ..Default::default() + }; + + let closer = dispatcher.dispatch_datagram(sess, Box::new(udp_stream)); + + // dispatcher -> tproxy + let responder = socket.clone(); + let fut1 = tokio::spawn(async move { + while let Some(pkt) = l_rx.recv().await { + trace!("tproxy <- dispatcher: {:?}", pkt); + + // remote -> local + match responder + .send_to(&pkt.data[..], pkt.dst_addr.must_into_socket_addr()) + .await + { + Ok(_) => {} + Err(e) => { + warn!("failed to send udp packet to proxy: {}", e); + } + } + } + }); + + // tproxy -> dispatcher + let fut2 = tokio::spawn(async move { + let mut buf = vec![0_u8; 1024 * 64]; + while let Ok(meta) = socket.recv_msg(&mut buf).await { + match meta.orig_dst { + Some(orig_dst) => { + if orig_dst.ip().is_multicast() + || match orig_dst.ip() { + std::net::IpAddr::V4(ip) => ip.is_broadcast(), + std::net::IpAddr::V6(_) => false, + } + { + continue; + } + + trace!("recv msg:{:?} orig_dst:{:?}", meta, orig_dst); + let pkt = UdpPacket { + data: buf[..meta.len].to_vec(), + src_addr: meta.addr.into(), + dst_addr: orig_dst.into(), + }; + trace!("tproxy -> dispatcher: {:?}", pkt); + match d_tx.send(pkt).await { + Ok(_) => {} + Err(e) => { + warn!("failed to send udp packet to proxy: {}", e); + continue; + } + } + } + None => { + warn!("failed to get orig_dst"); + continue; + } + } + } + + closer.send(0).ok(); + }); + + let _ = futures::future::join(fut1, fut2).await; + Ok(()) +} diff --git a/clash_lib/src/proxy/trojan/mod.rs b/clash_lib/src/proxy/trojan/mod.rs index 58ffce46..0ed834fb 100644 --- a/clash_lib/src/proxy/trojan/mod.rs +++ b/clash_lib/src/proxy/trojan/mod.rs @@ -25,8 +25,8 @@ use super::{ options::{GrpcOption, WsOption}, transport::{self, TLSOptions}, utils::{RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, - AnyStream, CommonOption, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, + AnyStream, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, }; mod datagram; @@ -40,7 +40,7 @@ pub enum Transport { pub struct HandlerOptions { pub name: String, - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, pub server: String, pub port: u16, pub password: String, @@ -215,9 +215,9 @@ impl OutboundHandler for Handler { resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -238,9 +238,9 @@ impl OutboundHandler for Handler { resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -254,7 +254,7 @@ impl OutboundHandler for Handler { } } -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use std::collections::HashMap; @@ -288,10 +288,6 @@ mod tests { #[tokio::test] #[serial_test::serial] async fn test_trojan_ws() -> anyhow::Result<()> { - let _ = tracing_subscriber::fmt() - // any additional configuration of the subscriber you might want here.. - .try_init(); - let span = tracing::info_span!("test_trojan_ws"); let _enter = span.enter(); diff --git a/clash_lib/src/proxy/tuic/mod.rs b/clash_lib/src/proxy/tuic/mod.rs index b532149f..9bde6f3a 100644 --- a/clash_lib/src/proxy/tuic/mod.rs +++ b/clash_lib/src/proxy/tuic/mod.rs @@ -54,9 +54,8 @@ use rustls::client::ClientConfig as TlsConfig; use self::types::{CongestionControl, TuicConnection, UdpRelayMode, UdpSession}; use super::{ - datagram::UdpPacket, - utils::{get_outbound_interface, Interface}, - AnyOutboundDatagram, CommonOption, ConnectorType, OutboundHandler, OutboundType, + datagram::UdpPacket, ConnectorType, HandlerCommonOptions, OutboundHandler, + OutboundType, }; #[derive(Debug, Clone)] @@ -81,7 +80,7 @@ pub struct HandlerOptions { pub receive_window: VarInt, #[allow(dead_code)] - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, /// not used #[allow(dead_code)] @@ -165,6 +164,7 @@ impl Handler { async fn init_endpoint( opts: HandlerOptions, resolver: ThreadSafeDNSResolver, + sess: &Session, ) -> Result { let mut crypto = TlsConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) @@ -198,22 +198,20 @@ impl Handler { quinn_config.transport_config(Arc::new(transport_config)); let socket = { - let iface = get_outbound_interface(); - if resolver.ipv6() { new_udp_socket( Some((Ipv6Addr::UNSPECIFIED, 0).into()), - iface.map(|x| Interface::Name(x.name.clone())), + sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await? } else { new_udp_socket( Some((Ipv4Addr::UNSPECIFIED, 0).into()), - iface.map(|x| Interface::Name(x.name.clone())), + sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await? } @@ -249,11 +247,12 @@ impl Handler { async fn get_conn( &self, resolver: &ThreadSafeDNSResolver, + sess: &Session, ) -> Result> { let endpoint = self .ep .get_or_try_init(|| { - Self::init_endpoint(self.opts.clone(), resolver.clone()) + Self::init_endpoint(self.opts.clone(), resolver.clone(), sess) }) .await?; @@ -282,7 +281,7 @@ impl Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> Result { - let conn = self.get_conn(&resolver).await?; + let conn = self.get_conn(&resolver, sess).await?; let dest = sess.destination.clone().into_tuic(); let tuic_tcp = conn.connect_tcp(dest).await?.compat(); let s = ChainedStreamWrapper::new(tuic_tcp); @@ -295,7 +294,7 @@ impl Handler { sess: &Session, resolver: ThreadSafeDNSResolver, ) -> Result { - let conn = self.get_conn(&resolver).await?; + let conn = self.get_conn(&resolver, sess).await?; let assos_id = self.next_assoc_id.fetch_add(1, Ordering::SeqCst); let quic_udp = TuicDatagramOutbound::new(assos_id, conn, sess.source.into()); let s = ChainedDatagramWrapper::new(quic_udp); @@ -311,12 +310,11 @@ struct TuicDatagramOutbound { } impl TuicDatagramOutbound { - #[allow(clippy::new_ret_no_self)] pub fn new( assoc_id: u16, conn: Arc, local_addr: ClashSocksAddr, - ) -> AnyOutboundDatagram { + ) -> Self { // TODO not sure about the size of buffer let (send_tx, send_rx) = tokio::sync::mpsc::channel::(32); let (recv_tx, recv_rx) = tokio::sync::mpsc::channel::(32); @@ -351,10 +349,10 @@ impl TuicDatagramOutbound { udp_sessions.write().await.remove(&assoc_id); anyhow::Ok(()) }); - let s = Self { + + Self { send_tx: tokio_util::sync::PollSender::new(send_tx), recv_rx, - }; - Box::new(s) + } } } diff --git a/clash_lib/src/proxy/tun/datagram.rs b/clash_lib/src/proxy/tun/datagram.rs index 07b22362..6692ace2 100644 --- a/clash_lib/src/proxy/tun/datagram.rs +++ b/clash_lib/src/proxy/tun/datagram.rs @@ -1,11 +1,8 @@ -use std::{net::SocketAddr, task::Poll}; +use std::task::Poll; use futures::{ready, Sink, Stream}; -use crate::{ - common::errors::new_io_error, - proxy::{datagram::UdpPacket, InboundDatagram}, -}; +use crate::{common::errors::new_io_error, proxy::datagram::UdpPacket}; #[derive(Debug)] pub struct TunDatagram { @@ -14,8 +11,6 @@ pub struct TunDatagram { pkt: Option, flushed: bool, - #[allow(unused)] - local_addr: SocketAddr, } impl TunDatagram { @@ -24,21 +19,16 @@ impl TunDatagram { tx: tokio::sync::mpsc::Sender, // receive from tun rx: tokio::sync::mpsc::Receiver, - // the address of the tun udp socket - local_addr: SocketAddr, ) -> Self { Self { rx, tx, pkt: None, flushed: true, - local_addr, } } } -impl InboundDatagram for TunDatagram {} - impl Stream for TunDatagram { type Item = UdpPacket; diff --git a/clash_lib/src/proxy/tun/inbound.rs b/clash_lib/src/proxy/tun/inbound.rs index 8af5167d..ab35677e 100644 --- a/clash_lib/src/proxy/tun/inbound.rs +++ b/clash_lib/src/proxy/tun/inbound.rs @@ -19,16 +19,17 @@ use crate::{ Error, Runner, }; -#[cfg(target_os = "macos")] -use crate::defer; -#[cfg(target_os = "macos")] -use crate::proxy::tun::routes; +use crate::{defer, proxy::tun::routes}; + +const DEFAULT_SO_MARK: u32 = 3389; +const DEFAULT_ROUTE_TABLE: u32 = 2468; async fn handle_inbound_stream( stream: netstack::TcpStream, local_addr: SocketAddr, remote_addr: SocketAddr, dispatcher: Arc, + so_mark: u32, ) { let sess = Session { network: Network::Tcp, @@ -43,6 +44,7 @@ async fn handle_inbound_stream( x ); }), + so_mark: Some(so_mark), ..Default::default() }; @@ -54,10 +56,9 @@ async fn handle_inbound_datagram( socket: Box, dispatcher: Arc, resolver: ThreadSafeDNSResolver, + so_mark: u32, ) { - let local_addr = socket.local_addr(); // tun i/o - let (ls, mut lr) = socket.split(); let ls = Arc::new(ls); @@ -70,7 +71,7 @@ async fn handle_inbound_datagram( // for dispatcher - the dispatcher would receive packets from this channel, // which is from the stack and send back packets to this channel, which // is to the tun - let udp_stream = TunDatagram::new(l_tx, d_rx, local_addr); + let udp_stream = TunDatagram::new(l_tx, d_rx); let sess = Session { network: Network::Udp, @@ -80,7 +81,7 @@ async fn handle_inbound_datagram( .inspect(|x| { debug!("selecting outbound interface: {:?} for tun UDP traffic", x); }), - + so_mark: Some(so_mark), ..Default::default() }; @@ -199,19 +200,29 @@ pub fn get_runner( let tun_name = tun.get_ref().name().map_err(map_io_error)?; info!("tun started at {}", tun_name); + let mut cfg = cfg; + cfg.route_table = cfg.route_table.or(Some(DEFAULT_ROUTE_TABLE)); + cfg.so_mark = cfg.so_mark.or(Some(DEFAULT_SO_MARK)); + maybe_add_routes(&cfg, &tun_name)?; let (stack, mut tcp_listener, udp_socket) = netstack::NetStack::with_buffer_size(512, 256).map_err(map_io_error)?; Ok(Some(Box::pin(async move { - #[cfg(target_os = "macos")] defer! { warn!("cleaning up routes"); - let _ = routes::maybe_routes_clean_up(); + match routes::maybe_routes_clean_up(&cfg) { + Ok(_) => {} + Err(e) => { + error!("failed to clean up routes: {}", e); + } + } } + let so_mark = cfg.so_mark.unwrap(); + let framed = tun.into_framed(); let (mut tun_sink, mut tun_stream) = framed.split(); @@ -273,6 +284,7 @@ pub fn get_runner( local_addr, remote_addr, dsp.clone(), + so_mark, )); } @@ -280,7 +292,7 @@ pub fn get_runner( })); futs.push(Box::pin(async move { - handle_inbound_datagram(udp_socket, dispatcher, resolver).await; + handle_inbound_datagram(udp_socket, dispatcher, resolver, so_mark).await; Err(Error::Operation("tun stopped unexpectedly 3".to_string())) })); diff --git a/clash_lib/src/proxy/tun/mod.rs b/clash_lib/src/proxy/tun/mod.rs index 19bc5b90..a8227a19 100644 --- a/clash_lib/src/proxy/tun/mod.rs +++ b/clash_lib/src/proxy/tun/mod.rs @@ -3,3 +3,172 @@ pub use netstack_lwip as netstack; mod datagram; pub use inbound::get_runner as get_tun_runner; mod routes; + +#[cfg(target_os = "linux")] // for tproxy +pub use datagram::TunDatagram; + +#[cfg(test)] +mod tests { + use std::thread; + + use crate::{shutdown, start, Config, Options}; + + fn wait_port_open(port: u16) { + let mut count = 0; + while count < 30 { + if std::net::TcpStream::connect(("127.0.0.1", port)).is_ok() { + break; + } + count += 1; + thread::sleep(std::time::Duration::from_secs(1)); + } + } + + #[test] + #[ignore = "only run this test locally, to not deal with the tun device \ + permission"] + fn test_individual_routes() { + let conf = r#" + socks-port: 7891 + bind-address: 127.0.0.1 + mmdb: "Country.mmdb" + log-level: trace + tun: + enable: true + device-id: "dev://utun1989" + route-all: false + routes: + - 1.1.1.1/32 + gateway: "198.19.0.1/32" + so-mark: 3389 + "#; + + let cwd = tempfile::tempdir() + .unwrap() + .path() + .to_str() + .unwrap() + .to_string(); + + let log_file = uuid::Uuid::new_v4().to_string() + ".log"; + + let cwd_clone = cwd.clone(); + let log_file_clone = log_file.clone(); + + let handle = thread::spawn(|| { + start(Options { + config: Config::Str(conf.to_string()), + cwd: Some(cwd_clone), + rt: None, + log_file: Some(log_file_clone), + }) + .unwrap() + }); + + wait_port_open(7891); + + let udp_socket = std::net::UdpSocket::bind("0.0.0.0:0").unwrap(); + let req = b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x05\x62\x61\x69\x64\x75\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"; + udp_socket + .send_to(req, "1.1.1.1:53") + .inspect_err(|x| { + panic!("failed to send udp packet: {x:?}"); + }) + .unwrap(); + + let mut buf = [0u8; 1024]; + udp_socket.recv_from(&mut buf).unwrap(); + + assert_eq!(buf[0], req[0]); + + assert!(shutdown()); + + thread::sleep(std::time::Duration::from_secs(1)); + + let today = chrono::Utc::now().format("%Y-%m-%d"); + + let log_path = cwd + "/" + &log_file + "." + &today.to_string(); + + let logs = std::fs::read_to_string(&log_path) + .unwrap_or_else(|_| panic!("failed to read log file: {}", log_path)); + + assert!(logs.contains("1.1.1.1:53 to MATCH")); + + handle.join().unwrap(); + } + + #[test] + #[ignore = "it's hard to test as altering the routing table can cause ssh \ + connection lost"] + fn test_route_all() { + let conf = r#" + socks-port: 7891 + bind-address: 127.0.0.1 + mmdb: "Country.mmdb" + log-level: trace + tun: + enable: true + device-id: "dev://utun1989" + route-all: true + gateway: "198.19.0.1/32" + so-mark: 3389 + "#; + + let cwd = tempfile::tempdir() + .unwrap() + .path() + .to_str() + .unwrap() + .to_string(); + + let log_file = uuid::Uuid::new_v4().to_string() + ".log"; + + let cwd_clone = cwd.clone(); + let log_file_clone = log_file.clone(); + + let handle = thread::spawn(|| { + start(Options { + config: Config::Str(conf.to_string()), + cwd: Some(cwd_clone), + rt: None, + log_file: Some(log_file_clone), + }) + .unwrap() + }); + + wait_port_open(7891); + + let echo_server = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); + let echo_addr = echo_server.local_addr().unwrap(); + thread::spawn(move || { + let mut buf = [0u8; 1024]; + loop { + let (n, src) = echo_server.recv_from(&mut buf).unwrap(); + echo_server.send_to(&buf[..n], src).unwrap(); + } + }); + + let udp_socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); + udp_socket.send_to(b"hello", echo_addr).unwrap(); + let mut buf = [0u8; 1024]; + udp_socket.recv_from(&mut buf).unwrap(); + + assert_eq!(b"hello", &buf[..5]); + + assert!(shutdown()); + + thread::sleep(std::time::Duration::from_secs(1)); + + let today = chrono::Utc::now().format("%Y-%m-%d"); + + let log_path = cwd + "/" + &log_file + "." + &today.to_string(); + + let logs = std::fs::read_to_string(&log_path) + .unwrap_or_else(|_| panic!("failed to read log file: {}", log_path)); + + assert!(logs.contains("route_all is enabled")); + assert!(logs.contains(format!("{} to MATCH", echo_addr).as_str())); + + handle.join().unwrap(); + } +} diff --git a/clash_lib/src/proxy/tun/routes/linux.rs b/clash_lib/src/proxy/tun/routes/linux.rs index 0ee6e018..1fb87deb 100644 --- a/clash_lib/src/proxy/tun/routes/linux.rs +++ b/clash_lib/src/proxy/tun/routes/linux.rs @@ -1,9 +1,174 @@ use ipnet::IpNet; use tracing::warn; -use crate::proxy::utils::OutboundInterface; +use crate::{ + common::errors::new_io_error, config::internal::config::TunConfig, + proxy::utils::OutboundInterface, +}; -pub fn add_route(_: &OutboundInterface, _: &IpNet) -> std::io::Result<()> { - warn!("add_route is not implemented on Linux"); +/// TODO: get rid of command execution +pub fn check_ip_command_installed() -> std::io::Result<()> { + std::process::Command::new("ip") + .arg("route") + .output() + .and_then(|output| { + if output.status.success() { + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "ip command not found", + )) + } + }) +} + +pub fn add_route(via: &OutboundInterface, dest: &IpNet) -> std::io::Result<()> { + let cmd = std::process::Command::new("ip") + .arg("route") + .arg("add") + .arg(dest.to_string()) + .arg("dev") + .arg(&via.name) + .output()?; + warn!("executing: ip route add {} dev {}", dest, via.name); + if !cmd.status.success() { + return Err(new_io_error(format!( + "add route failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } + Ok(()) +} + +/// three rules are added: +/// # ip route add default dev wg0 table 2468 +/// # ip rule add not fwmark 1234 table 2468 +/// # ip rule add table main suppress_prefixlength 0 +pub fn setup_policy_routing( + tun_cfg: &TunConfig, + via: &OutboundInterface, +) -> std::io::Result<()> { + let cmd = std::process::Command::new("ip") + .arg("route") + .arg("add") + .arg("default") + .arg("dev") + .arg(via.name.as_str()) + .arg("table") + .arg( + tun_cfg + .route_table + .expect("route_table not set") + .to_string(), + ) + .output()?; + warn!( + "executing: ip route add default dev {} table {}", + via.name, + tun_cfg.route_table.unwrap() + ); + if !cmd.status.success() { + return Err(new_io_error(format!( + "add default route failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } + + let cmd = std::process::Command::new("ip") + .arg("rule") + .arg("add") + .arg("not") + .arg("fwmark") + .arg(tun_cfg.so_mark.expect("so_mark not set").to_string()) + .arg("table") + .arg( + tun_cfg + .route_table + .expect("route_table not set") + .to_string(), + ) + .output()?; + warn!( + "executing: ip rule add not fwmark {} table {}", + tun_cfg.so_mark.unwrap(), + tun_cfg.route_table.unwrap() + ); + if !cmd.status.success() { + return Err(new_io_error(format!( + "add rule failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } + + let cmd = std::process::Command::new("ip") + .arg("rule") + .arg("add") + .arg("table") + .arg("main") + .arg("suppress_prefixlength") + .arg("0") + .output()?; + warn!("executing: ip rule add table main suppress_prefixlength 0"); + if !cmd.status.success() { + return Err(new_io_error(format!( + "add rule failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } + + Ok(()) +} + +/// three rules to clean up: +/// # ip rule add not fwmark $SO_MARK table $TABLE +/// # ip rule add table main suppress_prefixlength 0 +pub fn maybe_routes_clean_up(tun_cfg: &TunConfig) -> std::io::Result<()> { + if !(tun_cfg.enable && tun_cfg.route_all) { + return Ok(()); + } + + let cmd = std::process::Command::new("ip") + .arg("rule") + .arg("del") + .arg("not") + .arg("fwmark") + .arg(tun_cfg.so_mark.expect("so_mark not set").to_string()) + .arg("table") + .arg( + tun_cfg + .route_table + .expect("route_table not set") + .to_string(), + ) + .output()?; + warn!( + "executing: ip rule del not fwmark {} table {}", + tun_cfg.so_mark.unwrap(), + tun_cfg.route_table.unwrap() + ); + if !cmd.status.success() { + return Err(new_io_error(format!( + "delete rule failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } + + let cmd = std::process::Command::new("ip") + .arg("rule") + .arg("del") + .arg("table") + .arg("main") + .arg("suppress_prefixlength") + .arg("0") + .output()?; + + warn!("executing: ip rule del table main suppress_prefixlength 0"); + if !cmd.status.success() { + return Err(new_io_error(format!( + "delete rule failed: {}", + String::from_utf8_lossy(&cmd.stderr) + ))); + } Ok(()) } diff --git a/clash_lib/src/proxy/tun/routes/macos.rs b/clash_lib/src/proxy/tun/routes/macos.rs index e78c86c1..a6716b0b 100644 --- a/clash_lib/src/proxy/tun/routes/macos.rs +++ b/clash_lib/src/proxy/tun/routes/macos.rs @@ -5,6 +5,7 @@ use tracing::warn; use crate::{ common::errors::new_io_error, + config::internal::config::TunConfig, proxy::utils::{get_outbound_interface, OutboundInterface}, }; @@ -86,7 +87,7 @@ pub fn maybe_add_default_route() -> std::io::Result<()> { } /// failing to delete the default route won't cause route failure -pub fn maybe_routes_clean_up() -> std::io::Result<()> { +pub fn maybe_routes_clean_up(_: &TunConfig) -> std::io::Result<()> { let gateway = get_default_gateway()?; if let Some(gateway) = gateway { let default_interface = diff --git a/clash_lib/src/proxy/tun/routes/mod.rs b/clash_lib/src/proxy/tun/routes/mod.rs index e18dee30..6ab32c20 100644 --- a/clash_lib/src/proxy/tun/routes/mod.rs +++ b/clash_lib/src/proxy/tun/routes/mod.rs @@ -2,6 +2,8 @@ mod windows; #[cfg(windows)] use windows::add_route; +#[cfg(windows)] +pub use windows::maybe_routes_clean_up; #[cfg(target_os = "macos")] mod macos; @@ -14,14 +16,14 @@ pub use macos::maybe_routes_clean_up; mod linux; #[cfg(target_os = "linux")] use linux::add_route; +#[cfg(target_os = "linux")] +pub use linux::maybe_routes_clean_up; #[cfg(not(any(windows, target_os = "macos", target_os = "linux")))] mod other; #[cfg(not(any(windows, target_os = "macos", target_os = "linux")))] use other::add_route; -use std::net::Ipv4Addr; - use tracing::warn; use crate::{ @@ -29,11 +31,13 @@ use crate::{ proxy::utils::OutboundInterface, }; -use ipnet::IpNet; use network_interface::NetworkInterfaceConfig; pub fn maybe_add_routes(cfg: &TunConfig, tun_name: &str) -> std::io::Result<()> { if cfg.route_all || !cfg.routes.is_empty() { + #[cfg(target_os = "linux")] + linux::check_ip_command_installed()?; + let tun_iface = network_interface::NetworkInterface::show() .map_err(map_io_error)? .into_iter() @@ -57,23 +61,35 @@ pub fn maybe_add_routes(cfg: &TunConfig, tun_name: &str) -> std::io::Result<()> "route_all is enabled, all traffic will be routed through the tun \ interface" ); - let default_routes = vec![ - IpNet::new(std::net::IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 1) - .unwrap(), - IpNet::new(std::net::IpAddr::V4(Ipv4Addr::new(128, 0, 0, 0)), 1) - .unwrap(), - ]; - for r in default_routes { - add_route(&tun_iface, &r).map_err(map_io_error)?; - } - #[cfg(target_os = "macos")] + #[cfg(not(target_os = "linux"))] + { + use ipnet::IpNet; + + use std::net::Ipv4Addr; + + let default_routes = vec![ + IpNet::new(std::net::IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 1) + .unwrap(), + IpNet::new(std::net::IpAddr::V4(Ipv4Addr::new(128, 0, 0, 0)), 1) + .unwrap(), + ]; + for r in default_routes { + add_route(&tun_iface, &r)?; + } + + #[cfg(target_os = "macos")] + { + macos::maybe_add_default_route()?; + } + } + #[cfg(target_os = "linux")] { - macos::maybe_add_default_route()?; + linux::setup_policy_routing(cfg, &tun_iface)?; } } else { for r in &cfg.routes { - add_route(&tun_iface, r).map_err(map_io_error)?; + add_route(&tun_iface, r)?; } } } diff --git a/clash_lib/src/proxy/tun/routes/windows.rs b/clash_lib/src/proxy/tun/routes/windows.rs index f91194e3..fc1e042f 100644 --- a/clash_lib/src/proxy/tun/routes/windows.rs +++ b/clash_lib/src/proxy/tun/routes/windows.rs @@ -13,7 +13,10 @@ use windows::Win32::{ Networking::WinSock::{AF_INET, AF_INET6, PROTO_IP_RIP}, }; -use crate::{common::errors::new_io_error, defer, proxy::utils::OutboundInterface}; +use crate::{ + common::errors::new_io_error, config::internal::config::TunConfig, defer, + proxy::utils::OutboundInterface, +}; const PROTO_TYPE_UCAST: u32 = 0; const PROTO_VENDOR_ID: u32 = 0xFFFF; @@ -48,6 +51,10 @@ pub fn add_route(via: &OutboundInterface, dest: &IpNet) -> io::Result<()> { } } +pub fn maybe_routes_clean_up(_: &TunConfig) -> std::io::Result<()> { + Ok(()) +} + /// Add a route to the routing table. /// https://learn.microsoft.com/en-us/windows/win32/rras/add-and-update-routes-using-rtmaddroutetodest /// FIXME: figure out why this doesn't work https://stackoverflow.com/questions/43632619/how-to-properly-use-rtmv2-and-rtmaddroutetodest diff --git a/clash_lib/src/proxy/utils/mod.rs b/clash_lib/src/proxy/utils/mod.rs index 2e98c56f..399e36ca 100644 --- a/clash_lib/src/proxy/utils/mod.rs +++ b/clash_lib/src/proxy/utils/mod.rs @@ -3,7 +3,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, }; -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] pub mod test_utils; mod platform; @@ -17,7 +17,7 @@ pub use proxy_connector::*; use serde::{Deserialize, Serialize}; pub use socket_helpers::*; -use tracing::{debug, trace}; +use tracing::trace; #[derive(Debug)] pub struct OutboundInterface { @@ -89,6 +89,18 @@ pub fn get_outbound_interface() -> Option { let priority = ["eth", "en", "pdp_ip"]; all_outbounds.sort_by(|left, right| { + match (left.addr_v6, right.addr_v6) { + (Some(_), None) => return std::cmp::Ordering::Less, + (None, Some(_)) => return std::cmp::Ordering::Greater, + (Some(left), Some(right)) => { + if left.is_unicast_global() && !right.is_unicast_global() { + return std::cmp::Ordering::Less; + } else if !left.is_unicast_global() && right.is_unicast_global() { + return std::cmp::Ordering::Greater; + } + } + _ => {} + } let left = priority .iter() .position(|x| left.name.contains(x)) @@ -101,7 +113,7 @@ pub fn get_outbound_interface() -> Option { left.cmp(&right) }); - debug!( + trace!( "sorted outbound interfaces: {:?}, took: {}ms", all_outbounds, now.elapsed().as_millis() diff --git a/clash_lib/src/proxy/utils/platform/win.rs b/clash_lib/src/proxy/utils/platform/win.rs index 78f7057d..848a0585 100644 --- a/clash_lib/src/proxy/utils/platform/win.rs +++ b/clash_lib/src/proxy/utils/platform/win.rs @@ -1,6 +1,6 @@ use network_interface::NetworkInterfaceConfig; use std::{io, net::SocketAddr, os::windows::io::AsRawSocket}; -use tracing::{error, trace, warn}; +use tracing::{debug, error, warn}; use windows::Win32::{ Foundation::GetLastError, Networking::WinSock::{ @@ -8,7 +8,7 @@ use windows::Win32::{ }, }; -use crate::{common::errors::new_io_error, proxy::Interface}; +use crate::{common::errors::new_io_error, proxy::utils::Interface}; pub(crate) fn must_bind_socket_on_interface( socket: &socket2::Socket, @@ -19,17 +19,18 @@ pub(crate) fn must_bind_socket_on_interface( Interface::IpAddr(ip) => socket.bind(&SocketAddr::new(*ip, 0).into()), Interface::Name(name) => { // TODO: we should avoid calling `show` multiple times - let idx = network_interface::NetworkInterface::show() + let iface = network_interface::NetworkInterface::show() .map_err(|x| new_io_error(x.to_string().as_str()))? .into_iter() .find_map(|iface| { if &iface.name == name { - Some(iface.index) + Some(iface) } else { None } - }) - .unwrap_or_default(); + }); + + let idx = iface.as_ref().map(|iface| iface.index).unwrap_or_default(); if idx == 0 { warn!("failed to get interface index for {}", name); return Err(io::Error::new( @@ -38,7 +39,37 @@ pub(crate) fn must_bind_socket_on_interface( )); } - trace!("interface {} index is {}", name, idx); + let ip = match iface { + Some(iface) => iface.addr.iter().find_map(|addr| { + if family == socket2::Domain::IPV4 { + if addr.ip().is_ipv4() { + Some(addr.ip()) + } else { + None + } + } else if addr.ip().is_ipv6() { + Some(addr.ip()) + } else { + None + } + }), + None => None, + }; + + debug!( + "binding socket to interface: {}, index {}, ip {:?}", + name, idx, ip + ); + + if let Some(ip) = ip { + socket.bind(&SocketAddr::new(ip, 0).into())?; + } else { + warn!("failed to get address for interface {}", name); + return Err(io::Error::new( + io::ErrorKind::Other, + format!("failed to get address for interface {}", name), + )); + } let handle = SOCKET(socket.as_raw_socket().try_into().unwrap()); diff --git a/clash_lib/src/proxy/utils/proxy_connector.rs b/clash_lib/src/proxy/utils/proxy_connector.rs index 64837974..86396f0a 100644 --- a/clash_lib/src/proxy/utils/proxy_connector.rs +++ b/clash_lib/src/proxy/utils/proxy_connector.rs @@ -72,9 +72,7 @@ impl RemoteConnector for DirectConnector { address: &str, port: u16, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< - u32, - >, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> std::io::Result { let dial_addr = resolver .resolve(address, false) @@ -86,7 +84,7 @@ impl RemoteConnector for DirectConnector { (dial_addr, port).into(), iface.cloned(), #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + so_mark, ) .await .map(|x| Box::new(x) as _) @@ -98,15 +96,13 @@ impl RemoteConnector for DirectConnector { src: Option, _destination: SocksAddr, iface: Option, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< - u32, - >, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> std::io::Result { let dgram = new_udp_socket( src, iface, #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + so_mark, ) .await .map(|x| OutboundDatagramImpl::new(x, resolver))?; @@ -147,9 +143,7 @@ impl RemoteConnector for ProxyConnector { address: &str, port: u16, iface: Option<&Interface>, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< - u32, - >, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> std::io::Result { let sess = Session { network: Network::Tcp, @@ -157,7 +151,7 @@ impl RemoteConnector for ProxyConnector { destination: crate::session::SocksAddr::Domain(address.to_owned(), port), iface: iface.cloned(), #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + so_mark, ..Default::default() }; @@ -184,9 +178,7 @@ impl RemoteConnector for ProxyConnector { _src: Option, destination: SocksAddr, iface: Option, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option< - u32, - >, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> std::io::Result { let sess = Session { network: Network::Udp, @@ -194,7 +186,7 @@ impl RemoteConnector for ProxyConnector { iface, destination: destination.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - packet_mark, + so_mark, ..Default::default() }; let s = self diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index c6b9ed97..cd83d641 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -37,7 +37,7 @@ pub fn apply_tcp_options(s: TcpStream) -> std::io::Result { pub async fn new_tcp_stream( endpoint: SocketAddr, iface: Option, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> io::Result { let (socket, family) = match endpoint { SocketAddr::V4(_) => ( @@ -64,8 +64,8 @@ pub async fn new_tcp_stream( } #[cfg(any(target_os = "linux", target_os = "android"))] - if let Some(packet_mark) = packet_mark { - socket.set_mark(packet_mark)?; + if let Some(so_mark) = so_mark { + socket.set_mark(so_mark)?; } socket.set_keepalive(true)?; @@ -82,7 +82,7 @@ pub async fn new_tcp_stream( pub async fn new_udp_socket( src: Option, iface: Option, - #[cfg(any(target_os = "linux", target_os = "android"))] packet_mark: Option, + #[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option, ) -> io::Result { let (socket, family) = match src { Some(src) => { @@ -139,8 +139,8 @@ pub async fn new_udp_socket( } #[cfg(any(target_os = "linux", target_os = "android"))] - if let Some(packet_mark) = packet_mark { - socket.set_mark(packet_mark)?; + if let Some(so_mark) = so_mark { + socket.set_mark(so_mark)?; } socket.set_broadcast(true)?; @@ -148,41 +148,3 @@ pub async fn new_udp_socket( UdpSocket::from_std(socket.into()) } - -#[cfg(test)] -mod tests { - use std::{net::IpAddr, time::Duration}; - - use tokio::{net::TcpSocket, time::timeout}; - - #[tokio::test] - #[ignore = "not a real test"] - async fn test_connect_tcp() { - let mut futs = vec![]; - - for i in 0..100 { - futs.push(tokio::spawn(async move { - let now = std::time::Instant::now(); - let socket = socket2::Socket::new( - socket2::Domain::IPV4, - socket2::Type::DGRAM, - None, - ) - .unwrap(); - - timeout( - Duration::from_secs(10), - TcpSocket::from_std_stream(socket.into()) - .connect(("1.1.1.1".parse::().unwrap(), 443).into()), - ) - .await - .unwrap() - .unwrap(); - - println!("fut {} took {:?}", i, now.elapsed().as_millis()); - })); - } - - futures::future::join_all(futs).await; - } -} diff --git a/clash_lib/src/proxy/utils/test_utils/config_helper.rs b/clash_lib/src/proxy/utils/test_utils/config_helper.rs index 458e97eb..8b910ffd 100644 --- a/clash_lib/src/proxy/utils/test_utils/config_helper.rs +++ b/clash_lib/src/proxy/utils/test_utils/config_helper.rs @@ -24,10 +24,7 @@ pub fn test_config_base_dir() -> PathBuf { // load the config from test dir // and return the dns resolver for the proxy -pub async fn load_config() -> anyhow::Result<( - crate::config::internal::config::Config, - Arc, -)> { +pub async fn build_dns_resolver() -> anyhow::Result> { let root = root_dir(); let test_base_dir = test_config_base_dir(); let config_path = test_base_dir.join("ss.yaml").to_str().unwrap().to_owned(); @@ -51,9 +48,9 @@ pub async fn load_config() -> anyhow::Result<( ); let dns_resolver = Arc::new( - dns::EnhancedResolver::new(&config.dns, cache_store.clone(), mmdb.clone()) + dns::EnhancedResolver::new(config.dns, cache_store.clone(), mmdb.clone()) .await, ); - Ok((config, dns_resolver)) + Ok(dns_resolver) } diff --git a/clash_lib/src/proxy/utils/test_utils/docker_runner.rs b/clash_lib/src/proxy/utils/test_utils/docker_runner.rs index 3e9b583d..462d294c 100644 --- a/clash_lib/src/proxy/utils/test_utils/docker_runner.rs +++ b/clash_lib/src/proxy/utils/test_utils/docker_runner.rs @@ -305,32 +305,30 @@ impl DockerTestRunnerBuilder { } pub fn get_host_config(port: u16) -> HostConfig { - let mut host_config = HostConfig::default(); - // we need to use the host mode to enable the benchmark function - #[cfg(not(target_os = "macos"))] - { - host_config.network_mode = Some("host".to_owned()); + HostConfig { + port_bindings: Some( + [ + ( + (format!("{}/tcp", port)), + Some(vec![PortBinding { + host_ip: Some("0.0.0.0".to_owned()), + host_port: Some(format!("{}", port)), + }]), + ), + ( + (format!("{}/udp", port)), + Some(vec![PortBinding { + host_ip: Some("0.0.0.0".to_owned()), + host_port: Some(format!("{}", port)), + }]), + ), + ] + .into_iter() + .collect::>(), + ), + // we need to use the host mode to enable the benchmark function + #[cfg(not(target_os = "macos"))] + network_mode: Some("host".to_owned()), + ..Default::default() } - host_config.port_bindings = Some( - [ - ( - (format!("{}/tcp", port)), - Some(vec![PortBinding { - host_ip: Some("0.0.0.0".to_owned()), - host_port: Some(format!("{}", port)), - }]), - ), - ( - (format!("{}/udp", port)), - Some(vec![PortBinding { - host_ip: Some("0.0.0.0".to_owned()), - host_port: Some(format!("{}", port)), - }]), - ), - ] - .into_iter() - .collect::>(), - ); - - host_config } diff --git a/clash_lib/src/proxy/utils/test_utils/mod.rs b/clash_lib/src/proxy/utils/test_utils/mod.rs index cbab50b1..c54336b3 100644 --- a/clash_lib/src/proxy/utils/test_utils/mod.rs +++ b/clash_lib/src/proxy/utils/test_utils/mod.rs @@ -33,13 +33,20 @@ pub async fn ping_pong_test( // server(127.0.0.1:port) let sess = Session { - destination: ("127.0.0.1".to_owned(), port) + destination: ( + if cfg!(target_os = "linux") { + "127.0.0.1".to_owned() + } else { + "host.docker.internal".to_owned() + }, + port, + ) .try_into() .unwrap_or_else(|_| panic!("")), ..Default::default() }; - let (_, resolver) = config_helper::load_config().await?; + let resolver = config_helper::build_dns_resolver().await?; let listener = TcpListener::bind(format!("0.0.0.0:{}", port).as_str()).await?; @@ -144,7 +151,14 @@ pub async fn ping_pong_udp_test( let src = ("127.0.0.1".to_owned(), 10005) .try_into() .unwrap_or_else(|_| panic!("")); - let dst: SocksAddr = ("127.0.0.1".to_owned(), port) + let dst: SocksAddr = ( + if cfg!(target_os = "linux") { + "127.0.0.1".to_owned() + } else { + "host.docker.internal".to_owned() + }, + port, + ) .try_into() .unwrap_or_else(|_| panic!("")); @@ -153,7 +167,7 @@ pub async fn ping_pong_udp_test( ..Default::default() }; - let (_, resolver) = config_helper::load_config().await?; + let resolver = config_helper::build_dns_resolver().await?; let listener = UdpSocket::bind(format!("0.0.0.0:{}", port).as_str()).await?; info!("target local server started at: {}", listener.local_addr()?); @@ -228,7 +242,7 @@ pub async fn ping_pong_udp_test( pub async fn latency_test( handler: Arc, ) -> anyhow::Result<(u16, u16)> { - let (_, resolver) = config_helper::load_config().await?; + let resolver = config_helper::build_dns_resolver().await?; let proxy_manager = ProxyManager::new(resolver.clone()); proxy_manager .url_test(handler, "https://example.com", None) @@ -245,7 +259,7 @@ pub async fn dns_test(handler: Arc) -> anyhow::Result<()> { ..Default::default() }; - let (_, resolver) = config_helper::load_config().await?; + let resolver = config_helper::build_dns_resolver().await?; // we don't need the resolver, so it doesn't matter to create a casual one let stream = handler.connect_datagram(&sess, resolver).await?; @@ -341,8 +355,8 @@ pub async fn run_test_suites_and_cleanup( } Suite::DnsUdp => { let rv = dns_test(handler.clone()).await; - if rv.is_err() { - return Err(rv.unwrap_err()); + if let Err(rv) = rv { + return Err(rv); } else { tracing::info!("dns_test success"); } diff --git a/clash_lib/src/proxy/vmess/mod.rs b/clash_lib/src/proxy/vmess/mod.rs index a6fc1f24..bcf20dda 100644 --- a/clash_lib/src/proxy/vmess/mod.rs +++ b/clash_lib/src/proxy/vmess/mod.rs @@ -23,8 +23,8 @@ use super::{ options::{GrpcOption, Http2Option, HttpOption, WsOption}, transport::{self, Http2Config}, utils::{RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, - AnyStream, CommonOption, ConnectorType, DialWithConnector, OutboundHandler, - OutboundType, + AnyStream, ConnectorType, DialWithConnector, HandlerCommonOptions, + OutboundHandler, OutboundType, }; pub enum VmessTransport { @@ -37,7 +37,7 @@ pub enum VmessTransport { pub struct HandlerOptions { pub name: String, - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, pub server: String, pub port: u16, pub uuid: String, @@ -247,9 +247,9 @@ impl OutboundHandler for Handler { resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -270,9 +270,9 @@ impl OutboundHandler for Handler { resolver, self.opts.server.as_str(), self.opts.port, - self.opts.common_opts.iface.as_ref().or(sess.iface.as_ref()), + sess.iface.as_ref(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; @@ -286,7 +286,7 @@ impl OutboundHandler for Handler { } } -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use crate::proxy::utils::test_utils::{ config_helper::test_config_base_dir, @@ -317,10 +317,6 @@ mod tests { #[tokio::test] #[serial_test::serial] async fn test_vmess_ws() -> anyhow::Result<()> { - let _ = tracing_subscriber::fmt() - // any additional configuration of the subscriber you might want here.. - .try_init(); - let span = tracing::info_span!("test_vmess_ws"); let _enter = span.enter(); diff --git a/clash_lib/src/proxy/wg/mod.rs b/clash_lib/src/proxy/wg/mod.rs index a120eb28..6c028e02 100644 --- a/clash_lib/src/proxy/wg/mod.rs +++ b/clash_lib/src/proxy/wg/mod.rs @@ -21,7 +21,7 @@ use crate::{ use self::{keys::KeyBytes, wireguard::Config}; use super::{ - utils::RemoteConnector, CommonOption, ConnectorType, DialWithConnector, + utils::RemoteConnector, ConnectorType, DialWithConnector, HandlerCommonOptions, OutboundHandler, OutboundType, }; @@ -42,7 +42,7 @@ mod wireguard; pub struct HandlerOptions { pub name: String, - pub common_opts: CommonOption, + pub common_opts: HandlerCommonOptions, pub server: String, pub port: u16, pub ip: Ipv4Addr, @@ -93,9 +93,13 @@ impl Handler { } } + /// this is a one time initialization, however in theory sess.so_mark + /// and sess.iface should be all the same + /// ideally we move the so_mark and iface to a global context async fn initialize_inner( &self, resolver: ThreadSafeDNSResolver, + sess: &Session, ) -> Result<&Inner, Error> { self.inner .get_or_try_init(|| async { @@ -169,6 +173,7 @@ impl Handler { send_pair.1, resolver.clone(), self.connector.lock().await.as_ref().cloned(), + sess, ) .await .map_err(map_io_error)?; @@ -246,7 +251,7 @@ impl OutboundHandler for Handler { resolver: ThreadSafeDNSResolver, ) -> io::Result { let inner = self - .initialize_inner(resolver.clone()) + .initialize_inner(resolver.clone(), sess) .await .map_err(map_io_error)?; @@ -293,11 +298,11 @@ impl OutboundHandler for Handler { /// connect to remote target via UDP async fn connect_datagram( &self, - _sess: &Session, + sess: &Session, resolver: ThreadSafeDNSResolver, ) -> io::Result { let inner = self - .initialize_inner(resolver) + .initialize_inner(resolver, sess) .await .map_err(map_io_error)?; @@ -312,7 +317,7 @@ impl OutboundHandler for Handler { } } -#[cfg(all(test, not(ci)))] +#[cfg(all(test, docker_test))] mod tests { use crate::proxy::utils::{ diff --git a/clash_lib/src/proxy/wg/wireguard.rs b/clash_lib/src/proxy/wg/wireguard.rs index b028a71c..ec4277c6 100644 --- a/clash_lib/src/proxy/wg/wireguard.rs +++ b/clash_lib/src/proxy/wg/wireguard.rs @@ -31,7 +31,7 @@ use crate::{ utils::{RemoteConnector, GLOBAL_DIRECT_CONNECTOR}, AnyOutboundDatagram, }, - session::SocksAddr, + session::{Session, SocksAddr}, Error, }; @@ -83,6 +83,7 @@ impl WireguardTunnel { packet_reader: Receiver, resolver: ThreadSafeDNSResolver, connector: Option>, + sess: &Session, ) -> Result { let peer = Tunn::new( config.private_key, @@ -101,9 +102,9 @@ impl WireguardTunnel { resolver, None, remote_endpoint.into(), - None, // TODO: wg outbound interface https://github.com/Watfaq/clash-rs/issues/580 + sess.iface.clone(), #[cfg(any(target_os = "linux", target_os = "android"))] - None, + sess.so_mark, ) .await?; diff --git a/clash_lib/src/session.rs b/clash_lib/src/session.rs index 8b41fd83..ddce0385 100644 --- a/clash_lib/src/session.rs +++ b/clash_lib/src/session.rs @@ -47,11 +47,14 @@ impl SocksAddrType { impl SocksAddr { pub fn any_ipv4() -> Self { - Self::Ip("0.0.0.0:0".parse().unwrap()) + Self::Ip(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0)) } pub fn any_ipv6() -> Self { - Self::Ip("[::]:0".parse().unwrap()) + Self::Ip(SocketAddr::new( + IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + 0, + )) } pub fn write_buf(&self, buf: &mut T) { @@ -369,6 +372,8 @@ pub enum Type { HttpConnect, Socks5, Tun, + #[cfg(target_os = "linux")] + Tproxy, Ignore, } @@ -392,10 +397,14 @@ pub struct Session { pub source: SocketAddr, /// The proxy target address of a proxy connection. pub destination: SocksAddr, + /// The locally resolved IP address of the destination domain. + pub resolved_ip: Option, /// The packet mark SO_MARK - pub packet_mark: Option, + pub so_mark: Option, /// The bind interface pub iface: Option, + /// The ASN of the destination IP address. Only for display. + pub asn: Option, } impl Session { @@ -405,16 +414,23 @@ impl Session { rv.insert("type".to_string(), Box::new(self.typ) as _); rv.insert("sourceIP".to_string(), Box::new(self.source.ip()) as _); rv.insert("sourcePort".to_string(), Box::new(self.source.port()) as _); - rv.insert( - "destinationIP".to_string(), - Box::new(self.destination.ip()) as _, - ); + rv.insert("destinationIP".to_string(), { + let ip = self.resolved_ip.or(self.destination.ip()); + let asn = self.asn.clone(); + + let rv = match (ip, asn) { + (Some(ip), Some(asn)) => format!("{}({})", ip, asn), + (Some(ip), None) => ip.to_string(), + (None, _) => "".to_string(), + }; + Box::new(rv) as _ + }); rv.insert( "destinationPort".to_string(), Box::new(self.destination.port()) as _, ); rv.insert("host".to_string(), Box::new(self.destination.host()) as _); - + rv.insert("asn".to_string(), Box::new(self.asn.clone()) as _); rv } } @@ -426,8 +442,10 @@ impl Default for Session { typ: Type::Http, source: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), destination: SocksAddr::any_ipv4(), - packet_mark: None, + resolved_ip: None, + so_mark: None, iface: None, + asn: None, } } } @@ -448,8 +466,9 @@ impl Debug for Session { .field("network", &self.network) .field("source", &self.source) .field("destination", &self.destination) - .field("packet_mark", &self.packet_mark) + .field("packet_mark", &self.so_mark) .field("iface", &self.iface) + .field("asn", &self.asn) .finish() } } @@ -461,8 +480,10 @@ impl Clone for Session { typ: self.typ, source: self.source, destination: self.destination.clone(), - packet_mark: self.packet_mark, + resolved_ip: self.resolved_ip, + so_mark: self.so_mark, iface: self.iface.as_ref().cloned(), + asn: self.asn.clone(), } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5d56faf9..ec506292 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly" +channel = "nightly" # until https://github.com/rust-lang/rust-clippy/issues/13457