diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 00000000..c852e036 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,26 @@ +name: 'setup' +description: 'checkout, setup rust and python' +inputs: + rust-version: + description: 'Rust version' + required: true + default: 'stable' + extra-targets: + description: 'Extra Rust targets' + toolchain-args: + description: 'Extra args for `rustup toolchain`' +runs: + using: "composite" + steps: + - uses: mhils/workflows/setup-python@v12 # PyO3 wants recent Python on Windows. + - run: rustup toolchain install ${{ inputs.rust-version }} --profile minimal ${{ inputs.toolchain-args }} + shell: bash + - run: rustup default ${{ inputs.rust-version }} + shell: bash + - if: inputs.extra-targets + run: rustup target add ${{ inputs.extra-targets }} + shell: bash + - uses: mhils/workflows/rust-cache@v14 + - if: runner.os == 'Linux' + run: cargo install --locked bpf-linker + shell: bash diff --git a/.github/python-version.txt b/.github/python-version.txt index 2c073331..e4fba218 100644 --- a/.github/python-version.txt +++ b/.github/python-version.txt @@ -1 +1 @@ -3.11 +3.12 diff --git a/.github/scripts/pin-versions.py b/.github/scripts/pin-versions.py index 84ca6df8..36a80dd0 100644 --- a/.github/scripts/pin-versions.py +++ b/.github/scripts/pin-versions.py @@ -11,6 +11,7 @@ contents = ( contents .replace(f"mitmproxy_windows", f"mitmproxy_windows=={version}") + .replace(f"mitmproxy_linux", f"mitmproxy_linux=={version}") .replace(f"mitmproxy_macos", f"mitmproxy_macos=={version}") ) pyproject_toml.write_text(contents) diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 4ad7c9e6..1b32e18b 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -11,17 +11,18 @@ concurrency: cancel-in-progress: true env: - rust_clippy: "1.80" # MSRV + MSRV: "1.80" # Minimum Supported Rust Version jobs: protobuf: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: mhils/workflows/checkout@v12 - run: brew install swift-protobuf - run: cargo install protoc-gen-prost - run: protoc --proto_path=./src/ipc/ mitmproxy_ipc.proto --prost_out=./src/ipc/ + --prost_opt="bytes=data" --swift_out=./mitmproxy-macos/redirector/ipc - run: cargo fmt --all - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c @@ -35,18 +36,17 @@ jobs: - os: macos-latest - os: ubuntu-latest steps: - - uses: actions/checkout@v4 - - run: rustup toolchain install ${{ env.rust_clippy }} --profile minimal --component rustfmt --component clippy - - run: rustup default ${{ env.rust_clippy }} - - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - timeout-minutes: 2 - continue-on-error: true - # PyO3 wants recent Python on Windows. - - uses: actions/setup-python@v5 + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup with: - python-version-file: .github/python-version.txt - - - run: cargo clippy --fix --allow-dirty --workspace + rust-version: ${{ env.MSRV }} + toolchain-args: --component rustfmt --component clippy + + # We could run clippy on mitmproxy-linux-ebpf with + # cargo +nightly clippy --workspace -- -C panic=abort -Zpanic_abort_tests + # but that means we'd use nightly clippy, which may change its behavior (and thus break CI). + # So we rather exempt mitmproxy-linux-ebpf from clippy lints. + - run: cargo clippy --fix --allow-dirty --workspace --exclude mitmproxy-linux-ebpf - run: cargo fmt --all - run: git checkout src/ipc/mitmproxy_ipc.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8077ef9..9475fe62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,25 +22,27 @@ jobs: matrix: include: - os: windows-latest - rust: "1.80" # MSRV - args: --exclude macos-certificate-truster + rust: "1.80" # MSRV - can't use variables here. + args: --exclude mitmproxy-linux-ebpf - os: macos-latest - rust: "1.80" - args: --exclude windows-redirector + rust: "1.80" # MSRV - can't use variables here. + args: --exclude mitmproxy-linux-ebpf + - os: ubuntu-22.04 + rust: "1.80" # MSRV - can't use variables here. + args: --exclude mitmproxy-linux-ebpf - os: ubuntu-latest rust: stable - args: --exclude windows-redirector --exclude macos-certificate-truster + args: --exclude mitmproxy-linux-ebpf + - os: ubuntu-latest # old Ubuntu to test eBPF verifier compatibility + rust: nightly + args: --package mitmproxy-linux-ebpf + env: + RUSTFLAGS: ${{ matrix.rust == 'nightly' && '-Zpanic_abort_tests -C panic=abort' || '' }} steps: - - uses: actions/checkout@v4 - - name: Set up Rust toolchain - run: rustup toolchain install ${{ matrix.rust }} --profile minimal - - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - timeout-minutes: 2 - continue-on-error: true - # PyO3 wants recent Python on Windows. - - uses: actions/setup-python@v5 + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup with: - python-version-file: .github/python-version.txt + rust-version: ${{ matrix.rust }} - name: Run "cargo check" # the action-rs/cargo action adds inline annotations for "cargo check" output @@ -49,13 +51,19 @@ jobs: toolchain: ${{ matrix.rust }} command: check args: --workspace --verbose ${{ matrix.args }} - - name: Run "cargo test" + - if: matrix.rust != 'nightly' # XXX: weird errors here + name: Run "cargo test" # the action-rs/cargo action adds inline annotations for "cargo test" output uses: actions-rs/cargo@9e120dd99b0fbad1c065f686657e914e76bd7b72 with: toolchain: ${{ matrix.rust }} command: test args: --workspace --verbose ${{ matrix.args }} + - if: matrix.os == 'ubuntu-22.04' # Test that eBPF loads + run: cargo test --features root-tests + working-directory: mitmproxy-linux + env: + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: sudo -E build: strategy: @@ -69,7 +77,7 @@ jobs: - name: linux-arm64 os: ubuntu-latest target: aarch64-unknown-linux-gnu - args: --compatibility manylinux2014 --zig --target aarch64-unknown-linux-gnu + args: --compatibility manylinux2014 --zig --target aarch64-unknown-linux-gnu -i 3.12 - name: macos-universal os: macos-latest target: aarch64-apple-darwin x86_64-apple-darwin @@ -77,25 +85,17 @@ jobs: runs-on: ${{ matrix.os }} name: build mitmproxy-rs (${{ matrix.name }}) steps: - - uses: actions/checkout@v4 - - run: rustup toolchain install stable --profile minimal - - run: rustup default stable - - if: matrix.target - run: rustup target add ${{ matrix.target }} - - run: rustup show - - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - timeout-minutes: 2 - continue-on-error: true - - uses: actions/setup-python@v5 + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup with: - python-version-file: .github/python-version.txt + extra-targets: ${{ matrix.target }} - if: runner.os == 'Linux' name: Install maturin[zig] from PyPI - uses: install-pinned/maturin-with-zig@4804d730717f28b7565e71e68e5c9fe8c4f9b089 + uses: install-pinned/maturin-with-zig@68c027568b7d08df7bc3c52476ae28d1d2d787f5 - if: runner.os != 'Linux' name: Install maturin from PyPI - uses: install-pinned/maturin@c8c3a8f7f2b2ecf1728c96824734c6c6afe7e3e8 + uses: install-pinned/maturin@b1e3f698dbd19f284d4363cb361f75b2fa04679c - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') run: python .github/scripts/pin-versions.py @@ -106,7 +106,7 @@ jobs: # ensure that sdist is building. # We do this here instead of a separate job because we don't want to wait for the entire matrix. - if: contains(matrix.args, 'sdist') - run: pip install target/wheels/*.tar.gz + run: pip install --no-dependencies target/wheels/*.tar.gz - uses: actions/upload-artifact@v4 with: @@ -116,7 +116,7 @@ jobs: build-macos-app: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: mhils/workflows/checkout@v12 - uses: actions/cache@v4 id: cache-app with: @@ -137,62 +137,96 @@ jobs: name: macos-app path: mitmproxy-macos/redirector/dist/ - build-os-wheels: - needs: build-macos-app - strategy: - matrix: - include: - - os: windows - build-rust: --package windows-redirector - - os: macos - build-rust: --package macos-certificate-truster - runs-on: ${{ matrix.os }}-latest - name: build mitmproxy-${{ matrix.os }} + build-windows-wheel: + runs-on: windows-latest + name: build mitmproxy-windows steps: - - uses: actions/checkout@v4 - - # Build Rust - - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - timeout-minutes: 2 - continue-on-error: true - - run: rustup toolchain install stable --profile minimal - - run: rustup default stable - - if: runner.os == 'macOS' - run: rustup target add aarch64-apple-darwin x86_64-apple-darwin - - if: runner.os == 'macOS' - run: | - cargo build --release ${{ matrix.build-rust }} --target x86_64-apple-darwin - cargo build --release ${{ matrix.build-rust }} --target aarch64-apple-darwin - lipo -create -output target/release/macos-certificate-truster target/x86_64-apple-darwin/release/macos-certificate-truster target/aarch64-apple-darwin/release/macos-certificate-truster - - if: runner.os != 'macOS' - run: cargo build --release ${{ matrix.build-rust }} + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup + - uses: install-pinned/build@aa7fb973fec4a5593736c5dc25b322120ca41a98 + + - run: cargo build --release --package windows-redirector + + - run: python -m build --wheel ./mitmproxy-windows --outdir target/wheels/ + - uses: actions/upload-artifact@v4 + with: + name: wheels-os-windows + path: target/wheels + build-macos-wheel: + name: build mitmproxy-macos + needs: build-macos-app + runs-on: macos-latest + steps: + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup + with: + extra-targets: aarch64-apple-darwin x86_64-apple-darwin + - uses: install-pinned/build@aa7fb973fec4a5593736c5dc25b322120ca41a98 - # Download macOS app - - if: runner.os == 'macOS' - uses: actions/download-artifact@v4 + - run: | + cargo build --release --package macos-certificate-truster --target x86_64-apple-darwin + cargo build --release --package macos-certificate-truster --target aarch64-apple-darwin + lipo -create -output target/release/macos-certificate-truster target/x86_64-apple-darwin/release/macos-certificate-truster target/aarch64-apple-darwin/release/macos-certificate-truster + - uses: actions/download-artifact@v4 with: name: macos-app path: mitmproxy-macos/redirector/dist/ - # Build & upload wheel - - uses: actions/setup-python@v5 + - run: python -m build --wheel ./mitmproxy-macos --outdir target/wheels/ + - uses: actions/upload-artifact@v4 with: - python-version-file: .github/python-version.txt - - name: Install build from PyPI - uses: install-pinned/build@67059894d5ef2398af490c98fa8af8542d7b7008 - - run: python -m build --wheel ./mitmproxy-${{ matrix.os }} --outdir target/wheels/ + name: wheels-os-macos + path: target/wheels + + build-linux-wheel: + name: build mitmproxy-${{ matrix.name }} + strategy: + matrix: + include: + - name: linux-x86_64 + args: --compatibility manylinux2014 --zig --sdist + - name: linux-arm64 + target: aarch64-unknown-linux-gnu + args: --compatibility manylinux2014 --zig --target aarch64-unknown-linux-gnu -i 3.12 + runs-on: ubuntu-latest + steps: + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup + with: + extra-targets: ${{ matrix.target }} + - name: Install maturin[zig] from PyPI + uses: install-pinned/maturin-with-zig@68c027568b7d08df7bc3c52476ae28d1d2d787f5 + + - run: maturin build --release ${{ matrix.args }} + working-directory: ./mitmproxy-linux + - uses: actions/upload-artifact@v4 with: - name: wheels-os-${{ runner.os }} + name: wheels-os-${{ matrix.name }} + path: target/wheels + + test-linux-wheel-sdist: + needs: build-linux-wheel + runs-on: ubuntu-latest + steps: + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup + - uses: actions/download-artifact@v4 + with: + name: wheels-os-linux-x86_64 path: target/wheels + - run: pip install --no-dependencies target/wheels/*.tar.gz check: if: always() needs: - test + - test-linux-wheel-sdist - build - - build-os-wheels + - build-windows-wheel + - build-linux-wheel + - build-macos-wheel uses: mhils/workflows/.github/workflows/alls-green.yml@main with: jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a0c4bba6..278cc37c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,20 +19,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - timeout-minutes: 2 - continue-on-error: true - - name: Install maturin[zig] from PyPI - uses: install-pinned/maturin-with-zig@4804d730717f28b7565e71e68e5c9fe8c4f9b089 - - name: Install mypy from PyPI - uses: install-pinned/mypy@acfb567eb55a86c12a58ca1d28cd3e183e6a6d63 - - name: Install pdoc from PyPI - uses: install-pinned/pdoc@2e3e48c4027dad61fb97866e009e1e6b7da38209 + # TODO: This should ideally just reuse the main CI artifacts. + - uses: mhils/workflows/checkout@v12 + - uses: ./.github/actions/setup + - uses: install-pinned/maturin-with-zig@68c027568b7d08df7bc3c52476ae28d1d2d787f5 + - uses: install-pinned/mypy@2b552bed479e3f7065314667b670f7303619e989 + - uses: install-pinned/pdoc@fd9469ecb06f32b7012e07e449ce98b217bf1189 - run: maturin build working-directory: ./mitmproxy-rs - - run: pip install --no-index --find-links target/wheels/ mitmproxy_rs + - run: pip install --no-index --no-dependencies --find-links target/wheels/ mitmproxy_rs - run: stubtest --allowlist mitmproxy-rs/stubtest-allowlist.txt --mypy-config-file mitmproxy-rs/pyproject.toml mitmproxy_rs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b554578..fd5ecf96 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ re-run `maturin develop` and restart mitmproxy** for changes to apply. If you've followed the procedure above, you can run the basic test suite as follows: ```shell -cargo test --workspace +cargo test ``` Please ensure that all patches are accompanied by matching changes in the test suite. diff --git a/Cargo.lock b/Cargo.lock index 9a772c6a..9506ca00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -36,6 +36,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anes" version = "0.1.6" @@ -44,9 +50,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -59,43 +65,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" dependencies = [ "backtrace", ] @@ -108,9 +114,15 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "assert_matches" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-channel" @@ -126,9 +138,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -137,13 +149,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -154,13 +166,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -182,15 +194,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -207,17 +219,17 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper 1.0.1", - "tower", + "sync_wrapper", + "tower 0.5.2", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -228,24 +240,151 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper", "tower-layer", "tower-service", ] +[[package]] +name = "aya" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18bc4e506fbb85ab7392ed993a7db4d1a452c71b75a246af4a80ab8c9d2dd50" +dependencies = [ + "assert_matches", + "aya-obj", + "bitflags 2.6.0", + "bytes", + "libc", + "log", + "object", + "once_cell", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "aya-ebpf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8dbaf5409a1a0982e5c9bdc0f499a55fe5ead39fe9c846012053faf0d404f73" +dependencies = [ + "aya-ebpf-bindings", + "aya-ebpf-cty", + "aya-ebpf-macros", + "rustversion", +] + +[[package]] +name = "aya-ebpf-bindings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783dc1a82a3d71d83286165381dcc1b1d41643f4b110733d135547527c000a9a" +dependencies = [ + "aya-ebpf-cty", +] + +[[package]] +name = "aya-ebpf-cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cce099aaf3abb89f9a1f8594ffe07fa53738ebc2882fac624d10d9ba31a1b10" + +[[package]] +name = "aya-ebpf-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f47f7b4a75eb5f1d7ba0fb5628d247b1cf20388658899177875dabdda66865" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.92", +] + +[[package]] +name = "aya-log" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b600d806c1d07d3b81ab5f4a2a95fd80f479a0d3f1d68f29064d660865f85f02" +dependencies = [ + "aya", + "aya-log-common", + "bytes", + "log", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "aya-log-common" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befef9fe882e63164a2ba0161874e954648a72b0e1c4b361f532d590638c4eec" +dependencies = [ + "num_enum", +] + +[[package]] +name = "aya-log-ebpf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae348f459df78a79e5cd5e164b6562b927033b97ca3b033605b341a474f44510" +dependencies = [ + "aya-ebpf", + "aya-log-common", + "aya-log-ebpf-macros", +] + +[[package]] +name = "aya-log-ebpf-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d8251a75f56077db51892041aa6b77c70ef2723845d7a210979700b2f01bc4" +dependencies = [ + "aya-log-common", + "aya-log-parser", + "proc-macro2", + "quote", + "syn 2.0.92", +] + +[[package]] +name = "aya-log-parser" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b102eb5c88c9aa0b49102d3fbcee08ecb0dfa81014f39b373311de7a7032cb" +dependencies = [ + "aya-log-common", +] + +[[package]] +name = "aya-obj" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51b96c5a8ed8705b40d655273bc4212cbbf38d4e3be2788f36306f154523ec7" +dependencies = [ + "bytes", + "core-error", + "hashbrown 0.15.2", + "log", + "object", + "thiserror 1.0.69", +] + [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -346,9 +485,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -364,9 +503,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "c2rust-bitfields" @@ -388,6 +527,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.9", +] + [[package]] name = "cast" version = "0.3.0" @@ -396,9 +567,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.104" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -491,21 +665,21 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.8" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstyle", - "clap_lex 0.7.1", + "clap_lex 0.7.4", ] [[package]] @@ -519,9 +693,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cocoa" @@ -555,9 +729,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "concurrent-queue" @@ -607,6 +781,21 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "const-sha1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8a42181e0652c2997ae4d217f25b63c5337a52fd2279736e97b832fa0a3cff" + +[[package]] +name = "core-error" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efcdb2972eb64230b4c50646d8498ff73f5128d196a90c7236eec4cbe8619b8f" +dependencies = [ + "version_check", +] + [[package]] name = "core-foundation" version = "0.10.0" @@ -649,9 +838,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -674,10 +863,10 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.8", + "clap 4.5.23", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -698,23 +887,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -731,9 +920,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -775,7 +964,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -786,9 +975,9 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "defmt" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" +checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -796,24 +985,24 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" +checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" dependencies = [ "defmt-parser", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "defmt-parser" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" +checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" dependencies = [ - "thiserror 1.0.61", + "thiserror 2.0.9", ] [[package]] @@ -836,6 +1025,17 @@ dependencies = [ "subtle", ] +[[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.92", +] + [[package]] name = "either" version = "1.13.0" @@ -844,31 +1044,37 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -885,12 +1091,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -915,9 +1121,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -925,15 +1131,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -946,21 +1152,21 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -972,6 +1178,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.5.0" @@ -990,7 +1202,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -1010,9 +1222,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1041,9 +1253,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1058,9 +1270,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "futures-core", "pin-project-lite", @@ -1074,7 +1286,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -1130,15 +1342,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1146,7 +1358,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.2.6", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1180,9 +1392,14 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hdrhistogram" @@ -1213,6 +1430,12 @@ 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.1.19" @@ -1224,9 +1447,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -1236,9 +1459,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" dependencies = [ "async-trait", "cfg-if", @@ -1247,11 +1470,11 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand", - "thiserror 1.0.61", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -1260,9 +1483,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" dependencies = [ "cfg-if", "futures-util", @@ -1274,16 +1497,16 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror 1.0.61", + "thiserror 1.0.69", "tokio", "tracing", ] [[package]] name = "hickory-server" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be0e43c556b9b3fdb6c7c71a9a32153a2275d02419e3de809e520bfcfe40c37" +checksum = "35e6d1c2df0614595224b32479c72dd6fc82c9bda85962907c45fdb95a691489" dependencies = [ "async-trait", "bytes", @@ -1292,7 +1515,7 @@ dependencies = [ "futures-util", "hickory-proto", "serde", - "thiserror 1.0.61", + "thiserror 1.0.69", "time", "tokio", "tokio-util", @@ -1321,9 +1544,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1355,9 +1578,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1373,9 +1596,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -1394,9 +1617,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper", "hyper-util", @@ -1407,9 +1630,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1420,29 +1643,147 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] [[package]] -name = "idna" -version = "0.4.0" +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", ] [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1470,12 +1811,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -1511,9 +1852,12 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" +checksum = "e5d80fade88dd420ce0d9ab6f7c58ef2272dde38db874657950f827d4982c817" +dependencies = [ + "rustversion", +] [[package]] name = "ip_network" @@ -1551,26 +1895,26 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -1581,11 +1925,20 @@ 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.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jpeg-decoder" @@ -1595,10 +1948,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1610,20 +1964,31 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", ] +[[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", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1636,6 +2001,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -1739,21 +2110,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ - "adler", + "adler2", "simd-adler32", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -1796,11 +2166,11 @@ dependencies = [ "pretty-hex", "prost", "rand", - "rand_core", "security-framework", "smoltcp", "socket2", "sysinfo", + "tempfile", "tokio", "tokio-util", "tun", @@ -1808,6 +2178,45 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "mitmproxy-linux" +version = "0.12.0-dev" +dependencies = [ + "anyhow", + "aya", + "aya-log", + "cargo_metadata", + "const-sha1", + "env_logger", + "hex", + "internet-packet", + "libc", + "log", + "mitmproxy", + "mitmproxy-linux-ebpf", + "mitmproxy-linux-ebpf-common", + "prost", + "tokio", + "tun", +] + +[[package]] +name = "mitmproxy-linux-ebpf" +version = "0.12.0-dev" +dependencies = [ + "aya-ebpf", + "aya-log-ebpf", + "mitmproxy-linux-ebpf-common", + "which", +] + +[[package]] +name = "mitmproxy-linux-ebpf-common" +version = "0.12.0-dev" +dependencies = [ + "aya-ebpf", +] + [[package]] name = "mitmproxy_rs" version = "0.12.0-dev" @@ -1887,6 +2296,26 @@ dependencies = [ "autocfg", ] +[[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-macro2", + "quote", + "syn 2.0.92", +] + [[package]] name = "objc" version = "0.2.7" @@ -1898,10 +2327,13 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ + "crc32fast", + "hashbrown 0.15.2", + "indexmap 2.7.0", "memchr", ] @@ -1913,9 +2345,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "opaque-debug" @@ -1953,7 +2385,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.2", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -1966,29 +2398,29 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2009,9 +2441,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -2022,24 +2454,24 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.13" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2061,9 +2493,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -2073,9 +2505,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "pretty-hex" @@ -2092,7 +2527,6 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", "version_check", ] @@ -2107,6 +2541,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.92", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -2118,9 +2574,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" dependencies = [ "bytes", "prost-derive", @@ -2128,22 +2584,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "prost-types" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" dependencies = [ "prost", ] @@ -2234,7 +2690,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -2243,11 +2699,11 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -2258,9 +2714,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2317,32 +2773,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.2" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -2356,13 +2803,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2373,9 +2820,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "resolv-conf" @@ -2410,31 +2857,31 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -2459,9 +2906,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc" dependencies = [ "bitflags 2.6.0", "core-foundation", @@ -2472,9 +2919,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -2482,37 +2929,41 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +dependencies = [ + "serde", +] [[package]] name = "serde" -version = "1.0.203" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2526,6 +2977,12 @@ dependencies = [ "lazy_static", ] +[[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.2" @@ -2618,9 +3075,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ "proc-macro2", "quote", @@ -2629,21 +3086,26 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" [[package]] -name = "sync_wrapper" -version = "1.0.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", +] [[package]] name = "sysinfo" -version = "0.33.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" +checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" dependencies = [ "core-foundation-sys", "libc", @@ -2666,9 +3128,22 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] [[package]] name = "termcolor" @@ -2687,42 +3162,42 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.61", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.9", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -2748,9 +3223,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "num-conv", @@ -2765,6 +3240,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -2777,9 +3262,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -2792,9 +3277,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -2816,14 +3301,14 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -2832,9 +3317,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -2854,9 +3339,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -2876,7 +3361,7 @@ dependencies = [ "socket2", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -2902,23 +3387,37 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -2927,20 +3426,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -2948,9 +3447,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "once_cell", @@ -2969,9 +3468,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tun" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3f82bccbec181c56278683da7d915cf875a6cf8a450b3bcf1367de222775e" +checksum = "086997de063a84b0e446f64e63a32ba6fba894525bb3825158927e0d3a6fd610" dependencies = [ "bytes", "cfg-if", @@ -2981,7 +3480,7 @@ dependencies = [ "libc", "log", "nix 0.29.0", - "thiserror 2.0.3", + "thiserror 2.0.9", "tokio", "tokio-util", "windows-sys 0.59.0", @@ -2994,26 +3493,11 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unindent" @@ -3039,15 +3523,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3062,9 +3558,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -3093,34 +3589,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3128,28 +3624,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -3161,6 +3657,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "which" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + [[package]] name = "widestring" version = "1.1.0" @@ -3185,11 +3693,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3205,7 +3713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6b6833a760d1c36b489314a5541a12a39d162dc8341d8f6f400212b96d3df1" dependencies = [ "etherparse", - "thiserror 1.0.61", + "thiserror 1.0.69", "windivert-sys", "windows 0.48.0", ] @@ -3217,7 +3725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832bc4af9272458a8a64395b3aabe10dc4089546486fcbd0e19b9b6d28ba6e54" dependencies = [ "cc", - "thiserror 1.0.61", + "thiserror 1.0.69", "windows 0.48.0", ] @@ -3260,7 +3768,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -3271,7 +3779,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -3467,21 +3975,39 @@ dependencies = [ "toml", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wintun-bindings" -version = "0.7.16" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74675b7fccee92389d38c3d120445864d1085c421ee91c7ed05d66fb9bb76050" +checksum = "379a11db27bd8fb4d14ac1a445fc056234f9a3b60e38c8ce2a3d56110f62771b" dependencies = [ "blocking", "c2rust-bitfields", "futures", "libloading", "log", - "thiserror 1.0.61", + "thiserror 2.0.9", "windows-sys 0.59.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -3505,6 +4031,72 @@ dependencies = [ "rustix", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "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.92", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -3522,5 +4114,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", ] diff --git a/Cargo.toml b/Cargo.toml index 12379734..588e0142 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,21 @@ members = [ ".", "mitmproxy-rs", + "mitmproxy-linux", + "mitmproxy-linux-ebpf", + "mitmproxy-linux-ebpf-common", + "mitmproxy-macos/certificate-truster", "mitmproxy-windows/redirector", "wireguard-test-client", +] +default-members = [ + ".", + "mitmproxy-rs", + "mitmproxy-linux", + "mitmproxy-linux-ebpf-common", "mitmproxy-macos/certificate-truster", + "mitmproxy-windows/redirector", + "wireguard-test-client", ] [workspace.package] @@ -18,6 +30,13 @@ repository = "https://github.com/mitmproxy/mitmproxy-rs" edition = "2021" rust-version = "1.80" # MSRV +[workspace.dependencies] +aya = { version = "0.13.0", default-features = false } +aya-ebpf = { version = "0.1.1", default-features = false } +aya-log = { version = "0.2.1", default-features = false } +aya-log-ebpf = { version = "0.1.1", default-features = false } +tun = { version = "0.7.1" } + [workspace.lints.clippy] large_futures = "deny" @@ -36,7 +55,6 @@ anyhow = { version = "1.0.93", features = ["backtrace"] } log = "0.4.22" once_cell = "1" pretty-hex = "0.4.1" -rand_core = { version = "0.6.4", features = ["getrandom"] } smoltcp = "0.11" tokio = { version = "1.41.1", features = ["macros", "net", "rt-multi-thread", "sync", "time", "io-util", "process"] } boringtun = { version = "0.6", default-features = false } @@ -83,7 +101,8 @@ objc = "0.2" sysinfo = "0.33.0" [target.'cfg(target_os = "linux")'.dependencies] -tun = { version = "0.7.5", features = ["async"] } +tun = { workspace = true, features = ["async"] } +tempfile = "3.14.0" sysinfo = "0.33.0" [dev-dependencies] diff --git a/README.md b/README.md index 222b4da1..d4cd91af 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,21 @@ This repository contains mitmproxy's Rust bits, most notably: - [`src/`](./src): The `mitmproxy` crate containing most of the "meat". - [`mitmproxy-rs/`](./mitmproxy-rs): The `mitmproxy-rs` Python package, - which provides PyO3 bindings for the Rust crate. + which provides Python bindings for the Rust crate using [PyO3](https://pyo3.rs/). Source and binary distributions are available [on PyPI](https://pypi.org/project/mitmproxy-rs/). - [`mitmproxy-macos/`](./mitmproxy-macos): The `mitmproxy-macos` Python package, which contains a macOS Network Extension to transparently intercept macOS traffic. Only a binary distribution is available [on PyPI](https://pypi.org/project/mitmproxy-macos/) due to code signing and notarization requirements. - [`mitmproxy-windows/`](./mitmproxy-windows): The `mitmproxy-windows` Python package, which - contains the Windows traffic redirector based on WinDivert. + contains the Windows traffic redirector based on [WinDivert](https://github.com/basil00/WinDivert). Only a binary distribution is available [on PyPI](https://pypi.org/project/mitmproxy-windows/) due to build complexity. +- [`mitmproxy-linux/`](./mitmproxy-linux): The `mitmproxy-linux` Python package, which + contains the Linux traffic redirector based on [Aya](https://aya-rs.dev/). + Source and binary distributions are available [on PyPI](https://pypi.org/project/mitmproxy-linux/). +- [`mitmproxy-linux-ebpf/`](./mitmproxy-linux-ebpf): The eBPF program embedded in `mitmproxy-linux`. +- [`mitmproxy-linux-ebpf-common/`](./mitmproxy-linux-ebpf-common): Data structures shared by user space and eBPF. ### Architecture diff --git a/architecture.png b/architecture.png index a95f26e0..3b51859c 100644 Binary files a/architecture.png and b/architecture.png differ diff --git a/mitmproxy-linux-ebpf-common/Cargo.toml b/mitmproxy-linux-ebpf-common/Cargo.toml new file mode 100644 index 00000000..aed31120 --- /dev/null +++ b/mitmproxy-linux-ebpf-common/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "mitmproxy-linux-ebpf-common" +license = "MIT" +authors.workspace = true +version.workspace = true +repository.workspace = true +edition.workspace = true +rust-version.workspace = true +publish.workspace = true + +# aya-ebpf currently does not compile on Windows. +[target.'cfg(target_os = "linux")'.dependencies] +aya-ebpf = { workspace = true } + +[lib] +path = "src/lib.rs" diff --git a/mitmproxy-linux-ebpf-common/src/lib.rs b/mitmproxy-linux-ebpf-common/src/lib.rs new file mode 100644 index 00000000..0562ba79 --- /dev/null +++ b/mitmproxy-linux-ebpf-common/src/lib.rs @@ -0,0 +1,80 @@ +#![no_std] + +// aya-ebpf currently does not compile on Windows. +#[cfg(target_os = "linux")] +use aya_ebpf::TASK_COMM_LEN; +#[cfg(not(target_os = "linux"))] +const TASK_COMM_LEN: usize = 16; + +type Pid = u32; + +pub const INTERCEPT_CONF_LEN: u32 = 20; + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub enum Pattern { + Pid(Pid), + Process([u8; TASK_COMM_LEN]), +} + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub enum Action { + None, + Include(Pattern), + Exclude(Pattern), +} + +impl Pattern { + pub fn matches(&self, command: Option<&[u8; TASK_COMM_LEN]>, pid: Pid) -> bool { + match self { + Pattern::Pid(p) => pid == *p, + Pattern::Process(process) => { + let Some(command) = command else { + return false; + }; + // `command == process` inexplicably causes BPF verifier errors on Ubuntu 22.04, + // (it works on 24.04+), so we do a manual strcmp dance. + for i in 0..16 { + let curr = command[i]; + if curr != process[i] { + return false; + } + if curr == 0 { + break; + } + } + true + } + } + } +} + +impl From<&str> for Action { + fn from(value: &str) -> Self { + let value = value.trim(); + if let Some(value) = value.strip_prefix('!') { + Action::Exclude(Pattern::from(value)) + } else { + Action::Include(Pattern::from(value)) + } + } +} + +impl From<&str> for Pattern { + fn from(value: &str) -> Self { + let value = value.trim(); + match value.parse::() { + Ok(pid) => Pattern::Pid(pid), + Err(_) => { + let mut val = [0u8; TASK_COMM_LEN]; + let src = value.as_bytes(); + // This silently truncates to TASK_COMM_LEN - 1 bytes, + // bpf_get_current_comm always puts a null byte at the end. + let len = core::cmp::min(TASK_COMM_LEN - 1, src.len()); + val[..len].copy_from_slice(&src[..len]); + Pattern::Process(val) + } + } + } +} diff --git a/mitmproxy-linux-ebpf/.cargo/config.toml b/mitmproxy-linux-ebpf/.cargo/config.toml new file mode 100644 index 00000000..d8d7a20c --- /dev/null +++ b/mitmproxy-linux-ebpf/.cargo/config.toml @@ -0,0 +1,12 @@ +# We have this so that one doesn't need to manually pass +# --target=bpfel-unknown-none -Z build-std=core when running cargo +# check/build/doc etc. +# +# NB: this file gets loaded only if you run cargo from this directory, it's +# ignored if you run from the workspace root. See +# https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure +[build] +target = ["bpfeb-unknown-none", "bpfel-unknown-none"] + +[unstable] +build-std = ["core"] diff --git a/mitmproxy-linux-ebpf/Cargo.toml b/mitmproxy-linux-ebpf/Cargo.toml new file mode 100644 index 00000000..ed5360c5 --- /dev/null +++ b/mitmproxy-linux-ebpf/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "mitmproxy-linux-ebpf" +license = "MIT" +authors.workspace = true +version.workspace = true +repository.workspace = true +edition.workspace = true +rust-version.workspace = true +publish.workspace = true + +[dependencies] +mitmproxy-linux-ebpf-common = { path = "../mitmproxy-linux-ebpf-common" } + +aya-ebpf = { workspace = true } +aya-log-ebpf = { workspace = true } + +[build-dependencies] +which = "7.0.0" + +[[bin]] +name = "mitmproxy-linux" +path = "src/main.rs" diff --git a/mitmproxy-linux-ebpf/build.rs b/mitmproxy-linux-ebpf/build.rs new file mode 100644 index 00000000..f83c317a --- /dev/null +++ b/mitmproxy-linux-ebpf/build.rs @@ -0,0 +1,17 @@ +use which::which; + +/// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be +/// better expressed by [artifact-dependencies][bindeps] but issues such as +/// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. +/// +/// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the +/// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to +/// $PATH ahead of the one used as the cache key still exists. Solving this in the general case +/// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} +/// which would likely mean far too much cache invalidation. +/// +/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies +fn main() { + let bpf_linker = which("bpf-linker").unwrap(); + println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); +} diff --git a/mitmproxy-linux-ebpf/rust-toolchain.toml b/mitmproxy-linux-ebpf/rust-toolchain.toml new file mode 100644 index 00000000..f70d2254 --- /dev/null +++ b/mitmproxy-linux-ebpf/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["rust-src"] diff --git a/mitmproxy-linux-ebpf/src/lib.rs b/mitmproxy-linux-ebpf/src/lib.rs new file mode 100644 index 00000000..3ac3e595 --- /dev/null +++ b/mitmproxy-linux-ebpf/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +// This file exists to enable the library target. diff --git a/mitmproxy-linux-ebpf/src/main.rs b/mitmproxy-linux-ebpf/src/main.rs new file mode 100644 index 00000000..374ed7cc --- /dev/null +++ b/mitmproxy-linux-ebpf/src/main.rs @@ -0,0 +1,54 @@ +#![no_std] +#![no_main] + +use aya_ebpf::macros::{cgroup_sock, map}; +use aya_ebpf::maps::Array; +use aya_ebpf::programs::SockContext; +use aya_ebpf::EbpfContext; +use aya_log_ebpf::debug; +use mitmproxy_linux_ebpf_common::{Action, INTERCEPT_CONF_LEN}; + +#[no_mangle] +static INTERFACE_ID: u32 = 0; + +#[map] +static INTERCEPT_CONF: Array = Array::with_max_entries(INTERCEPT_CONF_LEN, 0); + +#[cgroup_sock(sock_create)] +pub fn cgroup_sock_create(ctx: SockContext) -> i32 { + if should_intercept(&ctx) { + debug!(&ctx, "intercepting in sock_create"); + let interface_id = unsafe { core::ptr::read_volatile(&INTERFACE_ID) }; + unsafe { + (*ctx.sock).bound_dev_if = interface_id; + } + } + 1 +} + +pub fn should_intercept(ctx: &SockContext) -> bool { + let command = ctx.command().ok(); + let pid = ctx.pid(); + + let mut intercept = matches!(INTERCEPT_CONF.get(0), Some(Action::Exclude(_))); + for i in 0..INTERCEPT_CONF_LEN { + match INTERCEPT_CONF.get(i) { + Some(Action::Include(pattern)) => { + intercept = intercept || pattern.matches(command.as_ref(), pid); + } + Some(Action::Exclude(pattern)) => { + intercept = intercept && !pattern.matches(command.as_ref(), pid); + } + _ => { + break; + } + } + } + intercept +} + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/mitmproxy-linux/Cargo.toml b/mitmproxy-linux/Cargo.toml new file mode 100644 index 00000000..0d6c9d62 --- /dev/null +++ b/mitmproxy-linux/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "mitmproxy-linux" +license = "MIT" +authors.workspace = true +version.workspace = true +repository.workspace = true +edition.workspace = true +rust-version.workspace = true +publish.workspace = true + +[lints] +workspace = true + +[[bin]] +name = "mitmproxy-linux-redirector" +path = "src/main.rs" + +[target.'cfg(target_os = "linux")'.dependencies] +mitmproxy = { path = "../" } +mitmproxy-linux-ebpf-common = { path = "../mitmproxy-linux-ebpf-common"} +tun = { workspace = true, features = ["async"] } +aya = { workspace = true } +aya-log = { workspace = true } +tokio = { version = "1.40", features = ["macros", "net", "rt-multi-thread", "sync", "io-util", "signal"] } +anyhow = { version = "1.0.89", features = ["backtrace"] } +log = "0.4.22" +env_logger = "0.11.5" +prost = "0.13.3" +internet-packet = { version = "0.2.0", features = ["checksums"] } +libc = "0.2.155" +const-sha1 = "0.3.0" + +[target.'cfg(target_os = "linux")'.build-dependencies] +cargo_metadata = { version = "0.19.0", default-features = false } +mitmproxy-linux-ebpf = { path = "../mitmproxy-linux-ebpf" } + +[target.'cfg(target_os = "linux")'.dev-dependencies] +hex = "0.4.3" + +[features] +root-tests = [] diff --git a/mitmproxy-linux/README.md b/mitmproxy-linux/README.md new file mode 100644 index 00000000..59476c91 --- /dev/null +++ b/mitmproxy-linux/README.md @@ -0,0 +1,13 @@ +# mitmproxy-linux + +This package contains the following precompiled binaries for Linux: + + - `mitmproxy-linux-redirector`: A Rust executable that redirects traffic to mitmproxy via eBPF. + + +## Redirector Development Setup + +1. Install [bpf-linker](https://github.com/aya-rs/bpf-linker): `cargo install --locked bpf-linker` +2. Run `pip install -e .` to install `mitmproxy_linux` as editable. +3. Run something along the lines of `mitmdump --mode local:curl`. + You should see a `Development mode: Compiling mitmproxy-linux-redirector...` message. diff --git a/mitmproxy-linux/build.rs b/mitmproxy-linux/build.rs new file mode 100644 index 00000000..23de726b --- /dev/null +++ b/mitmproxy-linux/build.rs @@ -0,0 +1,135 @@ +#[cfg(target_os = "linux")] +use std::{ + env, fs, + io::{BufRead as _, BufReader}, + path::PathBuf, + process::{Child, Command, Stdio}, +}; + +#[cfg(target_os = "linux")] +use cargo_metadata::{ + Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target, +}; + +#[cfg(not(target_os = "linux"))] +fn main() {} + +/// Based on https://github.com/aya-rs/aya-template/blob/main/%7B%7Bproject-name%7D%7D/build.rs +#[cfg(target_os = "linux")] +fn main() { + let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap(); + let ebpf_package = packages + .into_iter() + .find(|Package { name, .. }| name == "mitmproxy-linux-ebpf") + .unwrap(); + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = PathBuf::from(out_dir); + + let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap(); + let target = if endian == "big" { + "bpfeb" + } else if endian == "little" { + "bpfel" + } else { + panic!("unsupported endian={:?}", endian) + }; + + let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + + let target = format!("{target}-unknown-none"); + + let Package { manifest_path, .. } = ebpf_package; + let ebpf_dir = manifest_path.parent().unwrap(); + + // We have a build-dependency on `mitmproxy-linux-ebpf`, so cargo will automatically rebuild us + // if `mitmproxy-linux-ebpf`'s *library* target or any of its dependencies change. Since we + // depend on `mitmproxy-linux-ebpf`'s *binary* targets, that only gets us half of the way. This + // stanza ensures cargo will rebuild us on changes to the binaries too, which gets us the + // rest of the way. + println!("cargo:rerun-if-changed={}", ebpf_dir.as_str()); + + let mut cmd = Command::new("cargo"); + cmd.args([ + "build", + "-Z", + "build-std=core", + "--bins", + "--message-format=json", + "--release", + "--target", + &target, + ]); + + cmd.env("CARGO_CFG_BPF_TARGET_ARCH", arch); + + // Workaround to make sure that the rust-toolchain.toml is respected. + for key in ["RUSTUP_TOOLCHAIN", "RUSTC", "RUSTC_WORKSPACE_WRAPPER"] { + cmd.env_remove(key); + } + cmd.current_dir(ebpf_dir); + + // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself. + let ebpf_target_dir = out_dir.join("mitmproxy-linux-ebpf"); + cmd.arg("--target-dir").arg(&ebpf_target_dir); + + let mut child = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}")); + let Child { stdout, stderr, .. } = &mut child; + + // Trampoline stdout to cargo warnings. + let stderr = stderr.take().unwrap(); + let stderr = BufReader::new(stderr); + let stderr = std::thread::spawn(move || { + for line in stderr.lines() { + let line = line.unwrap(); + let skip = line.contains("Compiling ") || line.contains("Finished `"); + if !skip { + println!("cargo:warning={line}"); + } + } + }); + + let stdout = stdout.take().unwrap(); + let stdout = BufReader::new(stdout); + let mut executables = Vec::new(); + for message in Message::parse_stream(stdout) { + #[allow(clippy::collapsible_match)] + match message.expect("valid JSON") { + Message::CompilerArtifact(Artifact { + executable, + target: Target { name, .. }, + .. + }) => { + if let Some(executable) = executable { + executables.push((name, executable.into_std_path_buf())); + } + } + Message::CompilerMessage(CompilerMessage { message, .. }) => { + for line in message.rendered.unwrap_or_default().split('\n') { + println!("cargo:warning={line}"); + } + } + Message::TextLine(line) => { + println!("cargo:warning={line}"); + } + _ => {} + } + } + + let status = child + .wait() + .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}")); + assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}"); + + stderr.join().map_err(std::panic::resume_unwind).unwrap(); + + for (name, binary) in executables { + let dst = out_dir.join(name); + let _: u64 = fs::copy(&binary, &dst) + .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}")); + } +} diff --git a/mitmproxy-linux/mitmproxy_linux/__init__.py b/mitmproxy-linux/mitmproxy_linux/__init__.py new file mode 100644 index 00000000..7c50254b --- /dev/null +++ b/mitmproxy-linux/mitmproxy_linux/__init__.py @@ -0,0 +1,37 @@ +import sys +import sysconfig +from pathlib import Path + + +def executable_path() -> Path: + """ + Return the Path for mitmproxy-linux-redirector. + + For PyInstaller binaries this is the bundled executable, + for wheels this is the file in the package, + for development setups this may invoke cargo to build it. + """ + + if getattr(sys, 'frozen', False) and (pyinstaller_dir := getattr(sys, '_MEIPASS')): + return Path(pyinstaller_dir) / "mitmproxy-linux-redirector" + else: + here = Path(__file__).parent.absolute() + scripts = Path(sysconfig.get_path("scripts")).absolute() + exe = scripts / "mitmproxy-linux-redirector" + + # Development path: This should never happen with precompiled wheels. + if not exe.exists() and (here / "../Cargo.toml").exists(): + import logging + import subprocess + + logger = logging.getLogger(__name__) + logger.warning("Development mode: Compiling mitmproxy-linux-redirector...") + + # Build Redirector + subprocess.run(["cargo", "build"], cwd=here.parent, check=True) + target_debug = here.parent.parent / "target/debug" + + logger.warning("Development mode: Using target/debug/linux-redirector...") + exe = target_debug / "mitmproxy-linux-redirector" + + return exe diff --git a/mitmproxy-linux/pyproject.toml b/mitmproxy-linux/pyproject.toml new file mode 100644 index 00000000..0df145e8 --- /dev/null +++ b/mitmproxy-linux/pyproject.toml @@ -0,0 +1,16 @@ +[build-system] +requires = ["maturin>=1.7,<2.0"] +build-backend = "maturin" + +[project] +name = "mitmproxy_linux" +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dynamic = ["version"] + +[tool.maturin] +bindings = "bin" diff --git a/mitmproxy-linux/src/main.rs b/mitmproxy-linux/src/main.rs new file mode 100644 index 00000000..428084f0 --- /dev/null +++ b/mitmproxy-linux/src/main.rs @@ -0,0 +1,7 @@ +#[cfg(target_os = "linux")] +include!("main2.rs"); + +#[cfg(not(target_os = "linux"))] +pub fn main() { + panic!("The Linux redirector works on Linux only."); +} diff --git a/mitmproxy-linux/src/main2.rs b/mitmproxy-linux/src/main2.rs new file mode 100644 index 00000000..8bb407e6 --- /dev/null +++ b/mitmproxy-linux/src/main2.rs @@ -0,0 +1,197 @@ +use std::{fs, iter}; +use std::fs::Permissions; +use anyhow::Context; +use anyhow::anyhow; +use anyhow::Result; +use aya::{Ebpf, EbpfLoader}; +use aya::maps::Array; +use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; +use aya::Btf; +use aya::programs::{links::CgroupAttachMode, CgroupSock}; +use log::{debug, warn, info, error}; +use prost::bytes::{Bytes, BytesMut}; +use tokio::net::UnixDatagram; +use tokio::select; +use mitmproxy::packet_sources::tun::create_tun_device; +use tun::AbstractDevice; +use prost::Message; +use tokio::io::AsyncReadExt; +use tokio::signal::unix::{signal, SignalKind}; +use mitmproxy::ipc::{PacketWithMeta, from_proxy}; +use mitmproxy::ipc::FromProxy; +use mitmproxy::packet_sources::IPC_BUF_SIZE; +use mitmproxy_linux_ebpf_common::{Action, INTERCEPT_CONF_LEN}; + +// We can't implement aya::Pod in mitmproxy-linux-ebpf-common, so we do it on a newtype. +// (see https://github.com/aya-rs/aya/pull/59) +#[derive(Copy, Clone)] +#[repr(transparent)] +struct ActionWrapper(Action); + +unsafe impl aya::Pod for ActionWrapper {} + +const BPF_PROG: &[u8] = aya::include_bytes_aligned!(concat!(env!("OUT_DIR"), "/mitmproxy-linux")); +const BPF_HASH: [u8; 20] = const_sha1::sha1(BPF_PROG).as_bytes(); + +fn load_bpf(device_index: u32) -> Result { + debug!("Loading BPF program ({:x})...", Bytes::from_static(&BPF_HASH)); + let mut ebpf = EbpfLoader::new() + .btf(Btf::from_sys_fs().ok().as_ref()) + .set_global("INTERFACE_ID", &device_index, true) + .load(BPF_PROG) + .context("failed to load eBPF program")?; + if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { + // This can happen if you remove all log statements from your eBPF program. + warn!("failed to initialize eBPF logger: {}", e); + } + + debug!("Attaching BPF_CGROUP_INET_SOCK_CREATE program..."); + let prog: &mut CgroupSock = ebpf.program_mut("cgroup_sock_create").context("failed to get cgroup_sock_create")?.try_into()?; + // root cgroup to get all events. + let cgroup = fs::File::open("/sys/fs/cgroup/").context("failed to open \"/sys/fs/cgroup/\"")?; + prog.load().context("failed to load cgroup_sock_create program")?; + prog.attach(&cgroup, CgroupAttachMode::Single).context("failed to attach cgroup_sock_create program")?; + Ok(ebpf) +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or("info") + ) + //.format_target(false) + .format_timestamp(None) + .init(); + + let args: Vec = std::env::args().collect(); + let pipe_dir = args + .get(1) + .map(PathBuf::from) + .with_context(|| format!("usage: {} ", args[0]))?; + let mitmproxy_addr = pipe_dir.join("mitmproxy"); + let redirector_addr = pipe_dir.join("redirector"); + + bump_memlock_rlimit(); + + debug!("Creating tun device..."); + let (mut device, name) = create_tun_device(None)?; + let device_index = device.tun_index().context("failed to get tun device index")? as u32; + debug!("Tun device created: {name} (id={device_index})"); + + let mut ebpf = load_bpf(device_index).context("eBPF initialization failed")?; + + debug!("Getting INTERCEPT_CONF map..."); + let mut intercept_conf = { + let map = ebpf.map_mut("INTERCEPT_CONF") + .context("couldn't get INTERCEPT_CONF map")?; + Array::<_, ActionWrapper>::try_from(map) + .context("Cannot cast INTERCEPT_CONF to Array")? + }; + + debug!("Connecting to {}...", mitmproxy_addr.display()); + let ipc = UnixDatagram::bind(&redirector_addr) + .with_context(|| format!("failed to bind to {}", redirector_addr.display()))?; + ipc.connect(&mitmproxy_addr) + .with_context(|| format!("failed to connect to {}", mitmproxy_addr.display()))?; + fs::set_permissions(&mitmproxy_addr, Permissions::from_mode(0o777))?; + fs::set_permissions(&redirector_addr, Permissions::from_mode(0o777))?; + println!("{}", redirector_addr.to_string_lossy()); + + // Exit cleanly on SIGINT/SIGTERM + tokio::spawn(async { + let mut sigint = signal(SignalKind::interrupt()).context("failed to register SIGINT listener").unwrap(); + let mut sigterm = signal(SignalKind::terminate()).context("failed to register SIGTERM listener").unwrap(); + select! { + _ = sigint.recv() => (), + _ = sigterm.recv() => (), + } + std::process::exit(0); + }); + + let mut ipc_buf = BytesMut::with_capacity(IPC_BUF_SIZE); + let mut dev_buf = BytesMut::with_capacity(IPC_BUF_SIZE); + + loop { + select! { + r = ipc.recv_buf(&mut ipc_buf) => { + match r { + Ok(len) if len > 0 => { + let Ok(FromProxy { message: Some(message)}) = FromProxy::decode(&mut ipc_buf) else { + return Err(anyhow!("Received invalid IPC message: {:?}", &ipc_buf[..len])); + }; + assert_eq!(ipc_buf.len(), 0); + // debug!("Received IPC message: {message:?}"); + + match message { + from_proxy::Message::Packet(packet) => { + // debug!("Forwarding Packet to device: {}", packet.data.len()); + device.send(&packet.data).await.context("failed to send packet")?; + } + from_proxy::Message::InterceptConf(conf) => { + debug!("Updating ebpf intercept conf: {conf:?}"); + if conf.actions.len() > INTERCEPT_CONF_LEN as usize { + error!("Truncating intercept conf to {INTERCEPT_CONF_LEN} elements."); + } + let actions = conf.actions + .iter() + .map(|s| Action::from(s.as_str())) + .chain(iter::once(Action::None)) + .take(INTERCEPT_CONF_LEN as usize); + for (i, action) in actions.enumerate() { + intercept_conf.set(i as u32, ActionWrapper(action), 0) + .context("failed to update INTERCEPT_CONF")?; + } + } + } + } + _ => { + info!("IPC read failed. Exiting."); + std::process::exit(0); + } + } + }, + // ... or process incoming packets + r = device.read_buf(&mut dev_buf) => { + r.context("TUN read() failed")?; + + let packet = PacketWithMeta { + data: dev_buf.split().freeze(), + tunnel_info: None, + }; + + packet.encode(&mut ipc_buf)?; + let encoded = ipc_buf.split(); + + // debug!("Sending packet to proxy: {} {:?}", encoded.len(), &encoded); + ipc.send(&encoded).await?; + }, + } + } +} + +/// Bump the memlock rlimit. This is needed for older kernels that don't use the +/// new memcg based accounting, see https://lwn.net/Articles/837122/ +fn bump_memlock_rlimit() { + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + if ret != 0 { + info!("remove limit on locked memory failed, ret is: {}", ret); + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg_attr(not(feature = "root-tests"), ignore)] + #[tokio::test] + async fn bpf_load() { + load_bpf(0).unwrap(); + } + +} \ No newline at end of file diff --git a/mitmproxy-macos/README.md b/mitmproxy-macos/README.md index 049d3d80..60f367b6 100644 --- a/mitmproxy-macos/README.md +++ b/mitmproxy-macos/README.md @@ -4,3 +4,8 @@ This package contains the following precompiled binaries for macOS: - `macos-certificate-truster.app`: A helper app written in Rust to mark the mitmproxy CA as trusted. - `Mitmproxy Redirector.app`: The app bundle that sets up and hosts the network extension for redirecting traffic. + +## Redirector Development Setup + +The macOS Network System Extension needs to be signed and notarized during development. +You need to reconfigure the XCode project to use your own (paid) Apple Developer Account. \ No newline at end of file diff --git a/mitmproxy-macos/redirector/ipc/mitmproxy_ipc.pb.swift b/mitmproxy-macos/redirector/ipc/mitmproxy_ipc.pb.swift index 866c15ce..ada1a4bd 100644 --- a/mitmproxy-macos/redirector/ipc/mitmproxy_ipc.pb.swift +++ b/mitmproxy-macos/redirector/ipc/mitmproxy_ipc.pb.swift @@ -50,7 +50,14 @@ struct MitmproxyIpc_TunnelInfo: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var pid: UInt32 = 0 + var pid: UInt32 { + get {return _pid ?? 0} + set {_pid = newValue} + } + /// Returns true if `pid` has been explicitly set. + var hasPid: Bool {return self._pid != nil} + /// Clears the value of `pid`. Subsequent reads from it will return its default value. + mutating func clearPid() {self._pid = nil} var processName: String { get {return _processName ?? String()} @@ -65,6 +72,7 @@ struct MitmproxyIpc_TunnelInfo: Sendable { init() {} + fileprivate var _pid: UInt32? = nil fileprivate var _processName: String? = nil } @@ -122,8 +130,6 @@ struct MitmproxyIpc_InterceptConf: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - var `default`: Bool = false - var actions: [String] = [] var unknownFields = SwiftProtobuf.UnknownStorage() @@ -324,7 +330,7 @@ extension MitmproxyIpc_TunnelInfo: SwiftProtobuf.Message, SwiftProtobuf._Message // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularUInt32Field(value: &self.pid) }() + case 1: try { try decoder.decodeSingularUInt32Field(value: &self._pid) }() case 2: try { try decoder.decodeSingularStringField(value: &self._processName) }() default: break } @@ -336,9 +342,9 @@ extension MitmproxyIpc_TunnelInfo: SwiftProtobuf.Message, SwiftProtobuf._Message // allocates stack space for every if/case branch local when no optimizations // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and // https://github.com/apple/swift-protobuf/issues/1182 - if self.pid != 0 { - try visitor.visitSingularUInt32Field(value: self.pid, fieldNumber: 1) - } + try { if let v = self._pid { + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1) + } }() try { if let v = self._processName { try visitor.visitSingularStringField(value: v, fieldNumber: 2) } }() @@ -346,7 +352,7 @@ extension MitmproxyIpc_TunnelInfo: SwiftProtobuf.Message, SwiftProtobuf._Message } static func ==(lhs: MitmproxyIpc_TunnelInfo, rhs: MitmproxyIpc_TunnelInfo) -> Bool { - if lhs.pid != rhs.pid {return false} + if lhs._pid != rhs._pid {return false} if lhs._processName != rhs._processName {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -458,8 +464,7 @@ extension MitmproxyIpc_Packet: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl extension MitmproxyIpc_InterceptConf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".InterceptConf" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "default"), - 2: .same(proto: "actions"), + 1: .same(proto: "actions"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -468,25 +473,20 @@ extension MitmproxyIpc_InterceptConf: SwiftProtobuf.Message, SwiftProtobuf._Mess // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularBoolField(value: &self.`default`) }() - case 2: try { try decoder.decodeRepeatedStringField(value: &self.actions) }() + case 1: try { try decoder.decodeRepeatedStringField(value: &self.actions) }() default: break } } } func traverse(visitor: inout V) throws { - if self.`default` != false { - try visitor.visitSingularBoolField(value: self.`default`, fieldNumber: 1) - } if !self.actions.isEmpty { - try visitor.visitRepeatedStringField(value: self.actions, fieldNumber: 2) + try visitor.visitRepeatedStringField(value: self.actions, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: MitmproxyIpc_InterceptConf, rhs: MitmproxyIpc_InterceptConf) -> Bool { - if lhs.`default` != rhs.`default` {return false} if lhs.actions != rhs.actions {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true diff --git a/mitmproxy-macos/redirector/network-extension/InterceptConf.swift b/mitmproxy-macos/redirector/network-extension/InterceptConf.swift index 68e9ae29..38893346 100644 --- a/mitmproxy-macos/redirector/network-extension/InterceptConf.swift +++ b/mitmproxy-macos/redirector/network-extension/InterceptConf.swift @@ -53,7 +53,8 @@ class InterceptConf { convenience init(from ipc: MitmproxyIpc_InterceptConf) throws { let actions = try ipc.actions.map { try Action(from: $0) } - self.init(defaultAction: ipc.default, actions: actions) + let defaultAction = ipc.actions[0].hasPrefix("!") + self.init(defaultAction: defaultAction, actions: actions) } /// Mirrored after the Rust implementation diff --git a/mitmproxy-rs/mitmproxy_rs/_pyinstaller/hook-mitmproxy_linux.py b/mitmproxy-rs/mitmproxy_rs/_pyinstaller/hook-mitmproxy_linux.py new file mode 100644 index 00000000..7d448c01 --- /dev/null +++ b/mitmproxy-rs/mitmproxy_rs/_pyinstaller/hook-mitmproxy_linux.py @@ -0,0 +1,9 @@ +import sysconfig +import os.path + +binaries = [ + ( + os.path.join(sysconfig.get_path("scripts"), "mitmproxy-linux-redirector"), + "." + ) +] diff --git a/mitmproxy-rs/pyproject.toml b/mitmproxy-rs/pyproject.toml index dc6d1f6c..71cfc8db 100644 --- a/mitmproxy-rs/pyproject.toml +++ b/mitmproxy-rs/pyproject.toml @@ -14,11 +14,13 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Development Status :: 5 - Production/Stable", ] dependencies = [ "mitmproxy_windows; os_name == 'nt'", + "mitmproxy_linux; sys_platform == 'linux'", "mitmproxy_macos; sys_platform == 'darwin'", ] diff --git a/mitmproxy-rs/pytests/test_task.rs b/mitmproxy-rs/pytests/test_task.rs index 38a86905..320e9ea9 100644 --- a/mitmproxy-rs/pytests/test_task.rs +++ b/mitmproxy-rs/pytests/test_task.rs @@ -10,7 +10,8 @@ mod tests { use pyo3::types::PyDict; use crate::logger::setup_logger; - use tokio::sync::broadcast; + + use mitmproxy::shutdown; use tokio::sync::mpsc; #[pyo3_asyncio_0_21::tokio::test] @@ -60,7 +61,7 @@ mod tests { { let (command_tx, _command_rx) = mpsc::unbounded_channel(); let (event_tx, event_rx) = mpsc::channel(1); - let (shutdown_tx, shutdown_rx) = broadcast::channel(1); + let (shutdown_tx, shutdown_rx) = shutdown::channel(); let conn_handler = Python::with_gil(|py| { let locals = PyDict::new_bound(py); diff --git a/mitmproxy-rs/src/server/base.rs b/mitmproxy-rs/src/server/base.rs index 61c48414..ab8b85ef 100644 --- a/mitmproxy-rs/src/server/base.rs +++ b/mitmproxy-rs/src/server/base.rs @@ -6,13 +6,15 @@ use mitmproxy::packet_sources::{PacketSourceConf, PacketSourceTask}; use mitmproxy::shutdown::shutdown_task; use pyo3::prelude::*; +use mitmproxy::shutdown; +use tokio::sync::mpsc; +use tokio::sync::watch; use tokio::task::JoinSet; -use tokio::{sync::broadcast, sync::mpsc}; #[derive(Debug)] pub struct Server { - shutdown_done: broadcast::Receiver<()>, - start_shutdown: Option>, + shutdown_done: shutdown::Receiver, + start_shutdown: Option>, } impl Server { @@ -24,9 +26,9 @@ impl Server { } pub fn wait_closed<'py>(&self, py: Python<'py>) -> PyResult> { - let mut receiver = self.shutdown_done.resubscribe(); + let mut receiver = self.shutdown_done.clone(); pyo3_asyncio_0_21::tokio::future_into_py(py, async move { - receiver.recv().await.ok(); + receiver.recv().await; Ok(()) }) } @@ -51,13 +53,13 @@ impl Server { // This needs to be unbounded because write() is not async. let (transport_commands_tx, transport_commands_rx) = mpsc::unbounded_channel(); // Channel used to trigger graceful shutdown - let (shutdown_start_tx, shutdown_start_rx) = broadcast::channel(1); + let (shutdown_start_tx, shutdown_start_rx) = shutdown::channel(); let (packet_source_task, data) = packet_source_conf .build( transport_events_tx, transport_commands_rx, - shutdown_start_rx.resubscribe(), + shutdown_start_rx.clone(), ) .await?; @@ -75,7 +77,7 @@ impl Server { tasks.spawn(async move { packet_source_task.run().await }); tasks.spawn(async move { py_task.run().await }); - let (shutdown_done_tx, shutdown_done_rx) = broadcast::channel(1); + let (shutdown_done_tx, shutdown_done_rx) = shutdown::channel(); tokio::spawn(shutdown_task(tasks, shutdown_done_tx)); log::debug!("{} successfully initialized.", typ); diff --git a/mitmproxy-rs/src/server/local_redirector.rs b/mitmproxy-rs/src/server/local_redirector.rs index dc0500d2..3371406b 100644 --- a/mitmproxy-rs/src/server/local_redirector.rs +++ b/mitmproxy-rs/src/server/local_redirector.rs @@ -1,15 +1,13 @@ use mitmproxy::intercept_conf::InterceptConf; +#[cfg(target_os = "linux")] +use mitmproxy::packet_sources::linux::LinuxConf; #[cfg(target_os = "macos")] use mitmproxy::packet_sources::macos::MacosConf; #[cfg(windows)] use mitmproxy::packet_sources::windows::WindowsConf; use pyo3::prelude::*; -#[cfg(target_os = "macos")] -use std::path::Path; -#[cfg(windows)] -use std::path::PathBuf; use crate::server::base::Server; use tokio::sync::mpsc; @@ -70,15 +68,18 @@ impl LocalRedirector { #[cfg(any(windows, target_os = "macos"))] return None; - // #[cfg(target_os = "linux")] - // if !unistd::geteuid().is_root() { - // Some(String::from("mitmproxy is not running as root")) - // } else { - // None - // } + #[cfg(target_os = "linux")] + if nix::unistd::geteuid().is_root() { + None + } else { + Some("mitmproxy is not running as root.".to_string()) + } - #[cfg(not(any(windows, target_os = "macos")))] - Some(String::from("OS not supported for local redirect mode")) + #[cfg(not(any(windows, target_os = "macos", target_os = "linux")))] + Some(format!( + "Local redirect mode is not supported on {}", + std::env::consts::OS + )) } pub fn __repr__(&self) -> String { @@ -91,7 +92,7 @@ impl LocalRedirector { /// - `handle_tcp_stream`: An async function that will be called for each new TCP `Stream`. /// - `handle_udp_stream`: An async function that will be called for each new UDP `Stream`. /// -/// *Availability: Windows and macOS* +/// *Availability: Windows, Linux, and macOS* #[pyfunction] #[allow(unused_variables)] pub fn start_local_redirector( @@ -101,7 +102,7 @@ pub fn start_local_redirector( ) -> PyResult> { #[cfg(windows)] { - let executable_path: PathBuf = py + let executable_path: std::path::PathBuf = py .import_bound("mitmproxy_windows")? .call_method0("executable_path")? .extract()?; @@ -116,16 +117,33 @@ pub fn start_local_redirector( Ok(LocalRedirector::new(server, conf_tx)) }) } + #[cfg(target_os = "linux")] + { + let executable_path: std::path::PathBuf = py + .import_bound("mitmproxy_linux")? + .call_method0("executable_path")? + .extract()?; + if !executable_path.exists() { + return Err(anyhow::anyhow!("{} does not exist", executable_path.display()).into()); + } + let conf = LinuxConf { executable_path }; + pyo3_asyncio_0_21::tokio::future_into_py(py, async move { + let (server, conf_tx) = + Server::init(conf, handle_tcp_stream, handle_udp_stream).await?; + + Ok(LocalRedirector::new(server, conf_tx)) + }) + } #[cfg(target_os = "macos")] { let mut copy_task = None; - let destination_path = Path::new("/Applications/Mitmproxy Redirector.app"); + let destination_path = std::path::Path::new("/Applications/Mitmproxy Redirector.app"); if destination_path.exists() { log::info!("Using existing mitmproxy redirector app."); } else { let filename = py.import_bound("mitmproxy_macos")?.filename()?; - let source_path = Path::new(filename.to_str()?) + let source_path = std::path::Path::new(filename.to_str()?) .parent() .ok_or_else(|| anyhow::anyhow!("invalid path"))? .join("Mitmproxy Redirector.app.tar"); @@ -152,7 +170,7 @@ pub fn start_local_redirector( Ok(LocalRedirector::new(server, conf_tx)) }) } - #[cfg(not(any(windows, target_os = "macos")))] + #[cfg(not(any(windows, target_os = "macos", target_os = "linux")))] Err(pyo3::exceptions::PyNotImplementedError::new_err( LocalRedirector::unavailable_reason(), )) diff --git a/mitmproxy-rs/src/task.rs b/mitmproxy-rs/src/task.rs index f3a1b877..cf99b187 100644 --- a/mitmproxy-rs/src/task.rs +++ b/mitmproxy-rs/src/task.rs @@ -5,12 +5,12 @@ use anyhow::{Context, Result}; use pyo3::exceptions::asyncio::CancelledError; use pyo3::prelude::*; use pyo3_asyncio_0_21::TaskLocals; -use tokio::sync::{broadcast, mpsc, Mutex}; - -use mitmproxy::messages::{TransportCommand, TransportEvent}; +use tokio::sync::{mpsc, Mutex}; use crate::stream::Stream; use crate::stream::StreamState; +use mitmproxy::messages::{TransportCommand, TransportEvent}; +use mitmproxy::shutdown; pub struct PyInteropTask { locals: TaskLocals, @@ -18,7 +18,7 @@ pub struct PyInteropTask { transport_events: mpsc::Receiver, py_tcp_handler: PyObject, py_udp_handler: PyObject, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, } impl PyInteropTask { @@ -28,7 +28,7 @@ impl PyInteropTask { transport_events: mpsc::Receiver, py_tcp_handler: PyObject, py_udp_handler: PyObject, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Result { // Note: The current asyncio event loop needs to be determined here on the main thread. let locals = Python::with_gil(|py| -> Result { diff --git a/mitmproxy-rs/stubtest-allowlist.txt b/mitmproxy-rs/stubtest-allowlist.txt index 270360d5..5719d073 100644 --- a/mitmproxy-rs/stubtest-allowlist.txt +++ b/mitmproxy-rs/stubtest-allowlist.txt @@ -2,5 +2,6 @@ mitmproxy_rs.mitmproxy_rs mitmproxy_rs._pyinstaller.hook-mitmproxy_rs mitmproxy_rs._pyinstaller.hook-mitmproxy_windows mitmproxy_rs._pyinstaller.hook-mitmproxy_macos +mitmproxy_rs._pyinstaller.hook-mitmproxy_linux mitmproxy_rs.T mitmproxy_rs.dns.DnsResolver.__init__ diff --git a/mitmproxy-windows/mitmproxy_windows/__init__.py b/mitmproxy-windows/mitmproxy_windows/__init__.py index a91f446e..ab0e651f 100644 --- a/mitmproxy-windows/mitmproxy_windows/__init__.py +++ b/mitmproxy-windows/mitmproxy_windows/__init__.py @@ -12,7 +12,7 @@ def executable_path() -> Path: exe = here / "windows-redirector.exe" # Development path: This should never happen with precompiled wheels. - if not exe.exists() and (here / "editable.marker").exists(): + if not exe.exists() and (here / "../Cargo.toml").exists(): import logging import shutil import subprocess diff --git a/mitmproxy-windows/pyproject.toml b/mitmproxy-windows/pyproject.toml index c977eb3a..1e148633 100644 --- a/mitmproxy-windows/pyproject.toml +++ b/mitmproxy-windows/pyproject.toml @@ -14,7 +14,6 @@ Source = "https://github.com/mitmproxy/mitmproxy-rs" [tool.hatch.build] only-include = ["mitmproxy_windows"] -exclude = ["mitmproxy_windows/editable.marker"] [tool.hatch.version] path = "../Cargo.toml" diff --git a/mitmproxy-windows/redirector/src/main2.rs b/mitmproxy-windows/redirector/src/main2.rs index 632fa46b..315e4034 100644 --- a/mitmproxy-windows/redirector/src/main2.rs +++ b/mitmproxy-windows/redirector/src/main2.rs @@ -11,7 +11,7 @@ use lru_time_cache::LruCache; use mitmproxy::intercept_conf::{InterceptConf, ProcessInfo}; use mitmproxy::ipc; use mitmproxy::ipc::FromProxy; -use mitmproxy::packet_sources::windows::IPC_BUF_SIZE; +use mitmproxy::packet_sources::IPC_BUF_SIZE; use mitmproxy::windows::network::network_table; use mitmproxy::processes::get_process_name; use mitmproxy::MAX_PACKET_SIZE; @@ -329,7 +329,8 @@ async fn main() -> Result<()> { address.set_tcp_checksum(false); address.set_udp_checksum(false); - let packet = match InternetPacket::try_from(buf) { + // TODO: Use Bytes everywhere to avoid allocation. + let packet = match InternetPacket::try_from(buf.to_vec()) { Ok(p) => p, Err(e) => { info!("Error parsing packet: {:?}", e); @@ -550,9 +551,9 @@ async fn process_packet( } ipc_tx.send(ipc::PacketWithMeta { - data: packet.inner(), + data: packet.inner().into(), tunnel_info: Some(ipc::TunnelInfo { - pid: *pid, + pid: Some(*pid), process_name: process_name.clone(), }), })?; diff --git a/src/ipc/mitmproxy_ipc.proto b/src/ipc/mitmproxy_ipc.proto index a41ca5e7..f9951cda 100644 --- a/src/ipc/mitmproxy_ipc.proto +++ b/src/ipc/mitmproxy_ipc.proto @@ -13,7 +13,7 @@ message PacketWithMeta { TunnelInfo tunnel_info = 2; } message TunnelInfo { - uint32 pid = 1; + optional uint32 pid = 1; optional string process_name = 2; } @@ -30,8 +30,7 @@ message Packet { } // Intercept conf (macOS Control Stream) message InterceptConf { - bool default = 1; - repeated string actions = 2; + repeated string actions = 1; } // New flow (macOS TCP/UDP Stream) message NewFlow { diff --git a/src/ipc/mitmproxy_ipc.rs b/src/ipc/mitmproxy_ipc.rs index 8ccaed79..79284d96 100644 --- a/src/ipc/mitmproxy_ipc.rs +++ b/src/ipc/mitmproxy_ipc.rs @@ -8,15 +8,15 @@ /// Packet with associated tunnel info (Windows pipe to mitmproxy) #[derive(Clone, PartialEq, ::prost::Message)] pub struct PacketWithMeta { - #[prost(bytes = "vec", tag = "1")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes = "bytes", tag = "1")] + pub data: ::prost::bytes::Bytes, #[prost(message, optional, tag = "2")] pub tunnel_info: ::core::option::Option, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct TunnelInfo { - #[prost(uint32, tag = "1")] - pub pid: u32, + #[prost(uint32, optional, tag = "1")] + pub pid: ::core::option::Option, #[prost(string, optional, tag = "2")] pub process_name: ::core::option::Option<::prost::alloc::string::String>, } @@ -39,15 +39,13 @@ pub mod from_proxy { /// Packet (macOS UDP Stream) #[derive(Clone, PartialEq, ::prost::Message)] pub struct Packet { - #[prost(bytes = "vec", tag = "1")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes = "bytes", tag = "1")] + pub data: ::prost::bytes::Bytes, } /// Intercept conf (macOS Control Stream) #[derive(Clone, PartialEq, ::prost::Message)] pub struct InterceptConf { - #[prost(bool, tag = "1")] - pub default: bool, - #[prost(string, repeated, tag = "2")] + #[prost(string, repeated, tag = "1")] pub actions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } /// New flow (macOS TCP/UDP Stream) @@ -82,8 +80,8 @@ pub struct UdpFlow { } #[derive(Clone, PartialEq, ::prost::Message)] pub struct UdpPacket { - #[prost(bytes = "vec", tag = "1")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes = "bytes", tag = "1")] + pub data: ::prost::bytes::Bytes, #[prost(message, optional, tag = "2")] pub remote_address: ::core::option::Option
, } diff --git a/src/ipc/mod.rs b/src/ipc/mod.rs index e74e85fb..1d696c6d 100644 --- a/src/ipc/mod.rs +++ b/src/ipc/mod.rs @@ -25,7 +25,6 @@ impl From for Address { impl From for InterceptConf { fn from(conf: intercept_conf::InterceptConf) -> Self { InterceptConf { - default: conf.default(), actions: conf.actions(), } } diff --git a/src/messages.rs b/src/messages.rs index d48b8eca..4a5ed3bc 100755 --- a/src/messages.rs +++ b/src/messages.rs @@ -3,7 +3,7 @@ use std::fmt::Formatter; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use anyhow::{anyhow, Result}; -use internet_packet::InternetPacket; +use internet_packet::{InternetPacket, TransportProtocol}; use smoltcp::wire::{IpProtocol, Ipv4Packet, Ipv6Packet}; use tokio::sync::{mpsc, oneshot}; @@ -14,7 +14,7 @@ pub enum TunnelInfo { dst_addr: SocketAddr, }, LocalRedirector { - pid: u32, + pid: Option, process_name: Option, /// macOS TCP connections may not have a valid sockname, but /// an unresolved remote_endpoint instead. @@ -121,17 +121,19 @@ pub enum SmolPacket { impl fmt::Debug for SmolPacket { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("SmolPacket"); match TryInto::::try_into(self.clone()) { - Ok(p) => f - .debug_struct("SmolPacket") - .field("src", &p.src()) - .field("dst", &p.dst()) - .field("protocol", &p.protocol()) - .field("tcp_flags_str", &p.tcp_flag_str()) - .field("payload", &String::from_utf8_lossy(p.payload())) - .finish(), + Ok(p) => { + f.field("src", &p.src()); + f.field("dst", &p.dst()); + f.field("protocol", &p.protocol()); + if matches!(p.protocol(), TransportProtocol::Tcp) { + f.field("tcp_flags_str", &p.tcp_flag_str()); + } + f.field("payload", &String::from_utf8_lossy(p.payload())); + f.finish() + } Err(_) => f - .debug_struct("SmolPacket") .field("src_ip", &self.src_ip()) .field("dst_ip", &self.dst_ip()) .field("transport_protocol", &self.transport_protocol()) diff --git a/src/network/core.rs b/src/network/core.rs index f4d2b76f..5ff8a59d 100644 --- a/src/network/core.rs +++ b/src/network/core.rs @@ -20,7 +20,7 @@ pub struct NetworkStack<'a> { net_tx: Sender, } -impl<'a> NetworkStack<'a> { +impl NetworkStack<'_> { pub fn new(net_tx: Sender) -> Self { Self { tcp: TcpHandler::new(net_tx.clone()), diff --git a/src/network/icmp.rs b/src/network/icmp.rs index 71770753..a89e7b73 100644 --- a/src/network/icmp.rs +++ b/src/network/icmp.rs @@ -69,13 +69,17 @@ pub(super) fn handle_icmpv6_echo_request( } }; - // Checking that it is an ICMP Echo Request. - if input_icmpv6_packet.msg_type() != Icmpv6Message::EchoRequest { - log::debug!( - "Unsupported ICMPv6 packet of type: {}", - input_icmpv6_packet.msg_type() - ); - return None; + match input_icmpv6_packet.msg_type() { + Icmpv6Message::EchoRequest => (), + Icmpv6Message::RouterSolicit => { + // These happen in Linux local redirect mode, not investigated any further. + log::debug!("Ignoring ICMPv6 router solicitation."); + return None; + } + other => { + log::debug!("Unsupported ICMPv6 packet of type: {other}"); + return None; + } } // Creating fake response packet. diff --git a/src/network/task.rs b/src/network/task.rs index 693591e9..b1a194aa 100755 --- a/src/network/task.rs +++ b/src/network/task.rs @@ -4,8 +4,6 @@ use anyhow::Result; use std::time::Duration; use tokio::sync::{ - broadcast, - broadcast::Receiver as BroadcastReceiver, mpsc, mpsc::{Permit, Receiver, Sender, UnboundedReceiver}, }; @@ -13,6 +11,7 @@ use tokio::task::JoinHandle; use crate::messages::{NetworkCommand, NetworkEvent, TransportCommand, TransportEvent}; use crate::network::core::NetworkStack; +use crate::shutdown; pub struct NetworkTask<'a> { net_tx: Sender, @@ -20,7 +19,7 @@ pub struct NetworkTask<'a> { py_tx: Sender, py_rx: UnboundedReceiver, - shutdown: BroadcastReceiver<()>, + shutdown: shutdown::Receiver, io: NetworkStack<'a>, } @@ -28,12 +27,12 @@ pub struct NetworkTask<'a> { pub fn add_network_layer( transport_events_tx: Sender, transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, -) -> Result<( + shutdown: shutdown::Receiver, +) -> ( JoinHandle>, Sender, Receiver, -)> { +) { // initialize channels between the WireGuard server and the virtual network device let (network_events_tx, network_events_rx) = mpsc::channel(256); let (network_commands_tx, network_commands_rx) = mpsc::channel(256); @@ -44,9 +43,9 @@ pub fn add_network_layer( transport_events_tx, transport_commands_rx, shutdown, - )?; + ); let h = tokio::spawn(Box::pin(async move { task.run().await })); - Ok((h, network_events_tx, network_commands_rx)) + (h, network_events_tx, network_commands_rx) } impl NetworkTask<'_> { @@ -55,17 +54,17 @@ impl NetworkTask<'_> { net_rx: Receiver, py_tx: Sender, py_rx: UnboundedReceiver, - shutdown: BroadcastReceiver<()>, - ) -> Result { + shutdown: shutdown::Receiver, + ) -> Self { let io = NetworkStack::new(net_tx.clone()); - Ok(Self { + Self { net_tx, net_rx, py_tx, py_rx, shutdown, io, - }) + } } pub async fn run(mut self) -> Result<()> { diff --git a/src/network/tcp.rs b/src/network/tcp.rs index df1ee75c..36fae2ba 100644 --- a/src/network/tcp.rs +++ b/src/network/tcp.rs @@ -51,7 +51,7 @@ pub struct TcpHandler<'a> { active_connections: HashSet<(SocketAddr, SocketAddr)>, } -impl<'a> TcpHandler<'a> { +impl TcpHandler<'_> { pub fn new(net_tx: Sender) -> Self { let mut device = VirtualDevice::new(net_tx); diff --git a/src/network/tests.rs b/src/network/tests.rs index 763b54d9..5dcd5525 100755 --- a/src/network/tests.rs +++ b/src/network/tests.rs @@ -1,23 +1,22 @@ use std::net::{Ipv6Addr, SocketAddr}; +use super::task::NetworkTask; +use crate::messages::{ + NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, +}; +use crate::shutdown; use anyhow::{anyhow, Result}; use internet_packet::InternetPacket; use smoltcp::{phy::ChecksumCapabilities, wire::*}; +use tokio::sync::watch; use tokio::{ sync::{ - broadcast::{self, Sender as BroadcastSender}, mpsc::{channel, unbounded_channel, Receiver, Sender, UnboundedSender}, oneshot, }, task::JoinHandle, }; -use crate::messages::{ - NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, -}; - -use super::task::NetworkTask; - struct MockNetwork { wg_to_smol_tx: Sender, smol_to_wg_rx: Receiver, @@ -25,7 +24,7 @@ struct MockNetwork { py_to_smol_tx: UnboundedSender, smol_to_py_rx: Receiver, - sd_trigger: BroadcastSender<()>, + sd_trigger: watch::Sender<()>, handle: JoinHandle>, } @@ -37,7 +36,7 @@ impl MockNetwork { let (py_to_smol_tx, py_to_smol_rx) = unbounded_channel(); let (smol_to_py_tx, smol_to_py_rx) = channel(64); - let (sd_trigger, sd_watcher) = broadcast::channel(1); + let (sd_trigger, sd_watcher) = shutdown::channel(); let task = NetworkTask::new( smol_to_wg_tx, @@ -45,7 +44,7 @@ impl MockNetwork { smol_to_py_tx, py_to_smol_rx, sd_watcher, - )?; + ); let handle = tokio::spawn(task.run()); diff --git a/src/network/udp.rs b/src/network/udp.rs index 1f01a45c..1b85c600 100644 --- a/src/network/udp.rs +++ b/src/network/udp.rs @@ -265,6 +265,7 @@ mod tests { use super::*; use crate::packet_sources::udp::UdpConf; use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; + use crate::shutdown; use std::net::{IpAddr, Ipv4Addr}; use tokio::net::UdpSocket; @@ -316,7 +317,7 @@ mod tests { async fn test_udp_server_echo() -> anyhow::Result<()> { let (commands_tx, commands_rx) = tokio::sync::mpsc::unbounded_channel(); let (events_tx, mut events_rx) = tokio::sync::mpsc::channel(1); - let (shutdown_tx, shutdown_rx) = tokio::sync::broadcast::channel(10); + let (shutdown_tx, shutdown_rx) = shutdown::channel(); let (task, addr) = UdpConf { host: "127.0.0.1".to_string(), port: 0, diff --git a/src/network/virtual_device.rs b/src/network/virtual_device.rs index ac7cfce2..c87f1885 100755 --- a/src/network/virtual_device.rs +++ b/src/network/virtual_device.rs @@ -73,7 +73,7 @@ pub struct VirtualTxToken<'a> { permit: Permit<'a, NetworkCommand>, } -impl<'a> TxToken for VirtualTxToken<'a> { +impl TxToken for VirtualTxToken<'_> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/src/packet_sources/linux.rs b/src/packet_sources/linux.rs new file mode 100755 index 00000000..6030dfe0 --- /dev/null +++ b/src/packet_sources/linux.rs @@ -0,0 +1,217 @@ +use anyhow::{bail, Context, Result}; +use log::{debug, error, log, Level}; +use std::io::Error; +use std::net::Shutdown; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::process::Stdio; +use std::str::FromStr; +use std::task::Poll; +use std::time::Duration; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, BufReader, ReadBuf}; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; + +use crate::intercept_conf::InterceptConf; +use crate::messages::{TransportCommand, TransportEvent}; +use crate::packet_sources::{forward_packets, PacketSourceConf, PacketSourceTask}; +use crate::shutdown; +use tempfile::{tempdir, TempDir}; +use tokio::net::UnixDatagram; +use tokio::process::Command; +use tokio::time::timeout; + +async fn start_redirector( + executable: &Path, + listener_addr: &Path, + shutdown: shutdown::Receiver, +) -> Result { + debug!("Elevating privileges..."); + // Try to elevate privileges using a dummy sudo invocation. + // The idea here is to block execution and give the user time to enter their password. + // For now, we naively assume that all systems 1) have sudo and 2) timestamp_timeout > 0. + let mut sudo = Command::new("sudo") + .arg("echo") + .arg("-n") + .spawn() + .context("Failed to run sudo.")?; + sudo.stdin.take(); + if !sudo.wait().await.is_ok_and(|x| x.success()) { + bail!("Failed to elevate privileges"); + } + + debug!("Starting mitmproxy-linux-redirector..."); + let mut redirector_process = Command::new("sudo") + .arg("--non-interactive") + .arg("--preserve-env") + .arg(executable) + .arg(listener_addr) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to launch mitmproxy-linux-redirector.")?; + + let stdout = redirector_process.stdout.take().unwrap(); + let stderr = redirector_process.stderr.take().unwrap(); + let shutdown2 = shutdown.clone(); + tokio::spawn(async move { + let mut stderr = BufReader::new(stderr).lines(); + let mut level = Level::Error; + while let Ok(Some(line)) = stderr.next_line().await { + if shutdown2.is_shutting_down() { + // We don't want to log during exit, https://github.com/vorner/pyo3-log/issues/30 + eprintln!("{}", line); + continue; + } + + let new_level = line + .strip_prefix("[") + .and_then(|s| s.split_once(" ")) + .and_then(|(level, line)| { + Level::from_str(level) + .ok() + .map(|l| (l, line.trim_ascii_start())) + }); + if let Some((l, line)) = new_level { + level = l; + log!(level, "[{line}"); + } else { + log!(level, "{line}"); + } + } + }); + tokio::spawn(async move { + match redirector_process.wait().await { + Ok(status) if status.success() => { + if shutdown.is_shutting_down() { + // We don't want to log during exit, https://github.com/vorner/pyo3-log/issues/30 + } else { + debug!("[linux-redirector] exited successfully.") + } + } + other => { + if shutdown.is_shutting_down() { + eprintln!("[linux-redirector] exited during shutdown: {:?}", other) + } else { + error!("[linux-redirector] exited: {:?}", other) + } + } + } + }); + + timeout( + Duration::from_secs(5), + BufReader::new(stdout).lines().next_line(), + ) + .await + .context("failed to establish connection to Linux redirector")? + .context("failed to read redirector stdout")? + .map(PathBuf::from) + .context("redirector did not produce stdout") +} + +pub struct LinuxConf { + pub executable_path: PathBuf, +} + +// We implement AsyncRead/AsyncWrite for UnixDatagram to have a common interface +// with Windows' NamedPipeServer. +pub struct AsyncUnixDatagram(UnixDatagram); + +impl AsyncRead for AsyncUnixDatagram { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + self.0.poll_recv(cx, buf) + } +} +impl AsyncWrite for AsyncUnixDatagram { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> Poll> { + self.0.poll_send(cx, buf) + } + + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + self.0.poll_send_ready(cx) + } + + fn poll_shutdown( + self: Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> Poll> { + Poll::Ready(self.0.shutdown(Shutdown::Write)) + } +} + +impl PacketSourceConf for LinuxConf { + type Task = LinuxTask; + type Data = UnboundedSender; + + fn name(&self) -> &'static str { + "Linux proxy" + } + + async fn build( + self, + transport_events_tx: Sender, + transport_commands_rx: UnboundedReceiver, + shutdown: shutdown::Receiver, + ) -> Result<(Self::Task, Self::Data)> { + let datagram_dir = tempdir().context("failed to create temp dir")?; + + let channel = UnixDatagram::bind(datagram_dir.path().join("mitmproxy"))?; + let dst = + start_redirector(&self.executable_path, datagram_dir.path(), shutdown.clone()).await?; + + channel + .connect(&dst) + .with_context(|| format!("Failed to connect to redirector at {}", dst.display()))?; + + let (conf_tx, conf_rx) = unbounded_channel(); + + Ok(( + LinuxTask { + datagram_dir, + channel: AsyncUnixDatagram(channel), + transport_events_tx, + transport_commands_rx, + conf_rx, + shutdown, + }, + conf_tx, + )) + } +} + +pub struct LinuxTask { + datagram_dir: TempDir, + channel: AsyncUnixDatagram, + transport_events_tx: Sender, + transport_commands_rx: UnboundedReceiver, + conf_rx: UnboundedReceiver, + shutdown: shutdown::Receiver, +} + +impl PacketSourceTask for LinuxTask { + async fn run(self) -> Result<()> { + forward_packets( + self.channel, + self.transport_events_tx, + self.transport_commands_rx, + self.conf_rx, + self.shutdown, + ) + .await?; + drop(self.datagram_dir); + Ok(()) + } +} diff --git a/src/packet_sources/macos.rs b/src/packet_sources/macos.rs index 725f1b81..8a2947d6 100644 --- a/src/packet_sources/macos.rs +++ b/src/packet_sources/macos.rs @@ -6,10 +6,12 @@ use crate::intercept_conf::InterceptConf; use crate::ipc; use crate::ipc::{NewFlow, TcpFlow, UdpFlow}; use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; +use crate::shutdown; use anyhow::{bail, Context, Result}; use futures_util::SinkExt; use futures_util::StreamExt; +use prost::bytes::Bytes; use prost::bytes::BytesMut; use prost::Message; @@ -24,7 +26,7 @@ use crate::network::udp::ConnectionState; use tokio::process::Command; use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::sync::{broadcast, oneshot}; +use tokio::sync::oneshot; use tokio::task::JoinSet; use tokio::time::timeout; use tokio_util::codec::{Framed, LengthDelimitedCodec}; @@ -77,7 +79,7 @@ impl PacketSourceConf for MacosConf { self, transport_events_tx: Sender, _transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Result<(Self::Task, Self::Data)> { let listener_addr = format!("/tmp/mitmproxy-{}", std::process::id()); let listener = UnixListener::bind(&listener_addr)?; @@ -113,7 +115,7 @@ pub struct MacOsTask { connections: JoinSet>, transport_events_tx: Sender, conf_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, } impl PacketSourceTask for MacOsTask { @@ -141,7 +143,7 @@ impl PacketSourceTask for MacOsTask { let task = ConnectionTask::new( stream, self.transport_events_tx.clone(), - self.shutdown.resubscribe(), + self.shutdown.clone(), ); self.connections.spawn(task.run()); }, @@ -167,14 +169,14 @@ impl PacketSourceTask for MacOsTask { struct ConnectionTask { stream: UnixStream, events: Sender, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, } impl ConnectionTask { pub fn new( stream: UnixStream, events: Sender, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Self { Self { stream, @@ -269,7 +271,8 @@ impl ConnectionTask { } else if remote_address != dst_addr { bail!("UDP packet destinations do not match: {remote_address} -> {dst_addr}") } - state.add_packet(packet.data); + // TODO: Make ConnectionState accept Bytes, not Vec + state.add_packet(packet.data.to_vec()); }, Some(command) = command_rx.recv() => { match command { @@ -279,7 +282,7 @@ impl ConnectionTask { TransportCommand::WriteData(_, data) => { assert!(first_packet.is_none()); let packet = ipc::UdpPacket { - data, + data: Bytes::from(data), remote_address: Some(remote_address.into()), }; write_buf.reserve(packet.encoded_len()); @@ -316,7 +319,7 @@ impl ConnectionTask { let dst_addr = SocketAddr::try_from(&remote) .unwrap_or_else(|_| SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))); let tunnel_info = TunnelInfo::LocalRedirector { - pid: flow.tunnel_info.as_ref().map(|t| t.pid).unwrap_or(0), + pid: flow.tunnel_info.as_ref().and_then(|t| t.pid), process_name: flow.tunnel_info.and_then(|t| t.process_name), remote_endpoint: Some((remote.host, remote.port as u16)), }; diff --git a/src/packet_sources/mod.rs b/src/packet_sources/mod.rs index e5ade46d..45b71679 100755 --- a/src/packet_sources/mod.rs +++ b/src/packet_sources/mod.rs @@ -1,9 +1,20 @@ -use anyhow::Result; +use crate::intercept_conf::InterceptConf; +use crate::ipc::PacketWithMeta; +use crate::messages::{ + NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, +}; +use crate::network::add_network_layer; +use crate::{ipc, shutdown, MAX_PACKET_SIZE}; +use anyhow::{anyhow, Context, Result}; +use prost::bytes::{Bytes, BytesMut}; +use prost::Message; use std::future::Future; -use tokio::sync::{broadcast, mpsc}; - -use crate::messages::{TransportCommand, TransportEvent}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::sync::mpsc; +use tokio::sync::mpsc::{Sender, UnboundedReceiver}; +#[cfg(target_os = "linux")] +pub mod linux; #[cfg(target_os = "macos")] pub mod macos; #[cfg(target_os = "linux")] @@ -23,10 +34,102 @@ pub trait PacketSourceConf { self, transport_events_tx: mpsc::Sender, transport_commands_rx: mpsc::UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> impl Future> + Send; } pub trait PacketSourceTask: Send { fn run(self) -> impl Future> + Send; } + +pub const IPC_BUF_SIZE: usize = MAX_PACKET_SIZE + 1024; + +/// Feed packets from a socket into smol, and the other way around. +#[allow(dead_code)] +async fn forward_packets( + mut channel: T, + transport_events_tx: Sender, + transport_commands_rx: UnboundedReceiver, + mut conf_rx: UnboundedReceiver, + shutdown: shutdown::Receiver, +) -> Result<()> { + let mut buf = BytesMut::with_capacity(IPC_BUF_SIZE); + let (mut network_task_handle, net_tx, mut net_rx) = + add_network_layer(transport_events_tx, transport_commands_rx, shutdown); + + loop { + tokio::select! { + // Monitor the network task for errors or planned shutdown. + // This way we implicitly monitor the shutdown channel. + exit = &mut network_task_handle => break exit.context("network task panic")?.context("network task error")?, + // pipe through changes to the intercept list + Some(conf) = conf_rx.recv() => { + let msg = ipc::FromProxy { + message: Some(ipc::from_proxy::Message::InterceptConf(conf.into())), + }; + msg.encode(&mut buf)?; + + // debug!("Sending IPC message to redirector: {} {:?}", buf.len(), buf); + channel.write_all_buf(&mut buf).await.context("failed to propagate interception config update")?; + }, + // read packets from the IPC pipe into our network stack. + _ = channel.read_buf(&mut buf) => { + if buf.is_empty() { + // https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-client + // Because the client is reading from the pipe in message-read mode, it is + // possible for the ReadFile operation to return zero after reading a partial + // message. This happens when the message is larger than the read buffer. + // + // We don't support messages larger than the buffer, so this cannot happen. + // Instead, empty reads indicate that the IPC client has disconnected. + return Err(anyhow!("redirect daemon exited prematurely.")); + } + + let Ok(PacketWithMeta { data, tunnel_info}) = PacketWithMeta::decode(&mut buf) else { + return Err(anyhow!("Received invalid IPC message from redirector: {:?}", &buf)); + }; + assert!(buf.is_empty()); + + // TODO: Use Bytes in SmolPacket to avoid copy + let data = data.to_vec(); + + let Ok(mut packet) = SmolPacket::try_from(data) else { + log::error!("Skipping invalid packet: {:?}", &buf); + continue; + }; + + // debug!("Receiving packet: {:?}", &packet); + + // WinDivert packets do not have correct IP checksums yet, we need fix that here + // otherwise smoltcp will be unhappy with us. + packet.fill_ip_checksum(); + + let event = NetworkEvent::ReceivePacket { + packet, + tunnel_info: TunnelInfo::LocalRedirector { + pid: tunnel_info.as_ref().and_then(|t| t.pid), + process_name: tunnel_info.and_then(|t| t.process_name), + remote_endpoint: None, + }, + }; + if net_tx.try_send(event).is_err() { + log::warn!("Dropping incoming packet, TCP channel is full.") + }; + }, + // write packets from the network stack to the IPC pipe to be reinjected. + Some(e) = net_rx.recv() => { + match e { + NetworkCommand::SendPacket(packet) => { + let packet = ipc::FromProxy { message: Some(ipc::from_proxy::Message::Packet( ipc::Packet { data: Bytes::from(packet.into_inner()) }))}; + assert!(buf.is_empty()); + packet.encode(&mut buf)?; + // debug!("Sending packet: {} {:?}", buf.len(), &packet.message.as_ref().unwrap()); + channel.write_all_buf(&mut buf).await.context("failed to send packet")?; + } + } + } + } + } + log::info!("Redirector shutting down."); + Ok(()) +} diff --git a/src/packet_sources/tun.rs b/src/packet_sources/tun.rs index ba890ad0..638e14a9 100644 --- a/src/packet_sources/tun.rs +++ b/src/packet_sources/tun.rs @@ -3,11 +3,12 @@ use crate::messages::{ }; use crate::network::{add_network_layer, MAX_PACKET_SIZE}; use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; +use crate::shutdown; use anyhow::{Context, Result}; use std::cmp::max; use std::fs; +use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::{Permit, Receiver, UnboundedReceiver}; -use tokio::sync::{broadcast, mpsc::Sender}; use tun::AbstractDevice; pub struct TunConf { @@ -26,34 +27,12 @@ impl PacketSourceConf for TunConf { self, transport_events_tx: Sender, transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Result<(Self::Task, Self::Data)> { - let mut config = tun::Configuration::default(); - config.mtu(MAX_PACKET_SIZE as u16); - // Setting a local address and a destination is required on Linux. - config.address("169.254.0.1"); - // config.netmask("0.0.0.0"); - // config.destination("169.254.0.1"); - config.up(); - if let Some(tun_name) = self.tun_name { - config.tun_name(&tun_name); - } - - let device = tun::create_as_async(&config).context("Failed to create TUN device")?; - let tun_name = device.tun_name().context("Failed to get TUN name")?; - - if let Err(e) = disable_rp_filter(&tun_name) { - log::error!("failed to set rp_filter: {e}"); - } - if let Err(e) = fs::write( - format!("/proc/sys/net/ipv4/conf/{tun_name}/route_localnet"), - "1", - ) { - log::error!("Failed to enable route_localnet: {e}"); - } + let (device, tun_name) = create_tun_device(self.tun_name)?; let (network_task_handle, net_tx, net_rx) = - add_network_layer(transport_events_tx, transport_commands_rx, shutdown)?; + add_network_layer(transport_events_tx, transport_commands_rx, shutdown); Ok(( TunTask { @@ -67,6 +46,41 @@ impl PacketSourceConf for TunConf { } } +pub fn create_tun_device(tun_name: Option) -> Result<(tun::AsyncDevice, String)> { + let mut config = tun::Configuration::default(); + config.mtu(MAX_PACKET_SIZE as u16); + // Setting a local address and a destination is required on Linux. + config.address("169.254.0.1"); + // config.netmask("0.0.0.0"); + // config.destination("169.254.0.1"); + config.up(); + if let Some(tun_name) = tun_name { + config.tun_name(&tun_name); + } + + let device = tun::create_as_async(&config).context("Failed to create TUN device")?; + let tun_name = device.tun_name().context("Failed to get TUN name")?; + + if let Err(e) = disable_rp_filter(&tun_name) { + log::error!("failed to set rp_filter: {e}"); + } + if let Err(e) = fs::write( + format!("/proc/sys/net/ipv4/conf/{tun_name}/route_localnet"), + "1", + ) { + log::error!("Failed to enable route_localnet: {e}"); + } + // Update accept_local so that injected packets with a local address (e.g. 127.0.0.1) + // as source address are accepted. + if let Err(e) = fs::write( + format!("/proc/sys/net/ipv4/conf/{tun_name}/accept_local"), + "1", + ) { + log::error!("Failed to enable accept_local: {e}"); + } + Ok((device, tun_name)) +} + pub struct TunTask { device: tun::AsyncDevice, @@ -89,7 +103,7 @@ impl PacketSourceTask for TunTask { loop { tokio::select! { // Monitor the network task for errors or planned shutdown. - // This way we implicitly monitor the shutdown broadcast channel. + // This way we implicitly monitor the shutdown channel. exit = &mut self.network_task_handle => break exit.context("network task panic")?.context("network task error")?, // wait for transport_events_tx channel capacity... Ok(p) = self.net_tx.reserve(), if permit.is_none() => { diff --git a/src/packet_sources/udp.rs b/src/packet_sources/udp.rs index 63ac5e42..d654239a 100644 --- a/src/packet_sources/udp.rs +++ b/src/packet_sources/udp.rs @@ -3,17 +3,14 @@ use std::str::FromStr; use anyhow::{Context, Result}; -use socket2::{Domain, Protocol, Socket, Type}; -use tokio::sync::mpsc::{Permit, UnboundedReceiver}; -use tokio::{ - net::UdpSocket, - sync::{broadcast, mpsc::Sender}, -}; - use crate::messages::{TransportCommand, TransportEvent, TunnelInfo}; use crate::network::udp::{UdpHandler, UdpPacket}; use crate::network::MAX_PACKET_SIZE; use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; +use crate::shutdown; +use socket2::{Domain, Protocol, Socket, Type}; +use tokio::net::UdpSocket; +use tokio::sync::mpsc::{Permit, Sender, UnboundedReceiver}; pub fn remote_host_closed_conn(_res: &Result) -> bool { #[cfg(windows)] @@ -43,7 +40,7 @@ impl PacketSourceConf for UdpConf { self, transport_events_tx: Sender, transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Result<(Self::Task, Self::Data)> { let addr = format!("{}:{}", self.host, self.port); let sock_addr = SocketAddr::from_str(&addr).context("Invalid listen address specified")?; @@ -95,7 +92,7 @@ pub struct UdpTask { transport_events_tx: Sender, transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, } impl PacketSourceTask for UdpTask { diff --git a/src/packet_sources/windows.rs b/src/packet_sources/windows.rs index 6e91a59d..8d98a8ac 100755 --- a/src/packet_sources/windows.rs +++ b/src/packet_sources/windows.rs @@ -1,212 +1,133 @@ -use std::io::Cursor; -use std::iter; -use std::os::windows::ffi::OsStrExt; -use std::path::PathBuf; - -use anyhow::{anyhow, Context, Result}; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::windows::named_pipe::{NamedPipeServer, PipeMode, ServerOptions}; -use tokio::sync::broadcast; -use tokio::sync::mpsc::Sender; -use tokio::sync::mpsc::{unbounded_channel, Receiver, UnboundedReceiver, UnboundedSender}; -use windows::core::w; -use windows::core::PCWSTR; -use windows::Win32::UI::Shell::ShellExecuteW; -use windows::Win32::UI::Shell::SE_ERR_ACCESSDENIED; -use windows::Win32::UI::WindowsAndMessaging::{SW_HIDE, SW_SHOWNORMAL}; - -use crate::intercept_conf::InterceptConf; -use crate::ipc; -use crate::ipc::PacketWithMeta; -use crate::messages::{ - NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, -}; -use crate::network::{add_network_layer, MAX_PACKET_SIZE}; -use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; -use prost::Message; - -pub const IPC_BUF_SIZE: usize = MAX_PACKET_SIZE + 1024; - -pub struct WindowsConf { - pub executable_path: PathBuf, -} - -impl PacketSourceConf for WindowsConf { - type Task = WindowsTask; - type Data = UnboundedSender; - - fn name(&self) -> &'static str { - "Windows proxy" - } - - async fn build( - self, - transport_events_tx: Sender, - transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, - ) -> Result<(Self::Task, Self::Data)> { - let pipe_name = format!( - r"\\.\pipe\mitmproxy-transparent-proxy-{}", - std::process::id() - ); - - let ipc_server = ServerOptions::new() - .pipe_mode(PipeMode::Message) - .first_pipe_instance(true) - .max_instances(1) - .in_buffer_size(IPC_BUF_SIZE as u32) - .out_buffer_size(IPC_BUF_SIZE as u32) - .reject_remote_clients(true) - .create(&pipe_name)?; - - log::debug!("starting {} {}", self.executable_path.display(), pipe_name); - - let pipe_name = pipe_name - .encode_utf16() - .chain(iter::once(0)) - .collect::>(); - - let executable_path = self - .executable_path - .as_os_str() - .encode_wide() - .chain(iter::once(0)) - .collect::>(); - - let result = unsafe { - ShellExecuteW( - None, - w!("runas"), - PCWSTR::from_raw(executable_path.as_ptr()), - PCWSTR::from_raw(pipe_name.as_ptr()), - None, - if cfg!(debug_assertions) { - SW_SHOWNORMAL - } else { - SW_HIDE - }, - ) - }; - - if cfg!(debug_assertions) { - if result.0 <= 32 { - let err = windows::core::Error::from_win32(); - log::warn!("Failed to start child process: {}", err); - } - } else if result.0 == SE_ERR_ACCESSDENIED as isize { - return Err(anyhow!( - "Failed to start the interception process as administrator." - )); - } else if result.0 <= 32 { - let err = windows::core::Error::from_win32(); - return Err(anyhow!("Failed to start the executable: {}", err)); - } - - let (conf_tx, conf_rx) = unbounded_channel(); - - let (network_task_handle, net_tx, net_rx) = - add_network_layer(transport_events_tx, transport_commands_rx, shutdown)?; - - Ok(( - WindowsTask { - ipc_server, - buf: vec![0u8; IPC_BUF_SIZE], - net_tx, - net_rx, - conf_rx, - network_task_handle, - }, - conf_tx, - )) - } -} - -pub struct WindowsTask { - ipc_server: NamedPipeServer, - buf: Vec, - - net_tx: Sender, - net_rx: Receiver, - conf_rx: UnboundedReceiver, - network_task_handle: tokio::task::JoinHandle>, -} - -impl PacketSourceTask for WindowsTask { - async fn run(mut self) -> Result<()> { - log::debug!("Waiting for IPC connection..."); - self.ipc_server.connect().await?; - log::debug!("IPC connected!"); - - loop { - tokio::select! { - // Monitor the network task for errors or planned shutdown. - // This way we implicitly monitor the shutdown broadcast channel. - exit = &mut self.network_task_handle => break exit.context("network task panic")?.context("network task error")?, - // pipe through changes to the intercept list - Some(conf) = self.conf_rx.recv() => { - let msg = ipc::FromProxy { - message: Some(ipc::from_proxy::Message::InterceptConf(conf.into())), - }; - msg.encode(&mut self.buf.as_mut_slice())?; - let len = msg.encoded_len(); - - self.ipc_server.write_all(&self.buf[..len]).await?; - }, - // read packets from the IPC pipe into our network stack. - r = self.ipc_server.read(&mut self.buf) => { - let len = r.context("IPC read error.")?; - if len == 0 { - // https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-client - // Because the client is reading from the pipe in message-read mode, it is - // possible for the ReadFile operation to return zero after reading a partial - // message. This happens when the message is larger than the read buffer. - // - // We don't support messages larger than the buffer, so this cannot happen. - // Instead, empty reads indicate that the IPC client has disconnected. - return Err(anyhow!("redirect daemon exited prematurely.")); - } - - let mut cursor = Cursor::new(&self.buf[..len]); - let Ok(PacketWithMeta { data, tunnel_info: Some(ipc::TunnelInfo { pid, process_name })}) = PacketWithMeta::decode(&mut cursor) else { - return Err(anyhow!("Received invalid IPC message: {:?}", &self.buf[..len])); - }; - assert_eq!(cursor.position(), len as u64); - - let Ok(mut packet) = SmolPacket::try_from(data) else { - log::error!("Skipping invalid packet: {:?}", &self.buf[..len]); - continue; - }; - // WinDivert packets do not have correct IP checksums yet, we need fix that here - // otherwise smoltcp will be unhappy with us. - packet.fill_ip_checksum(); - - let event = NetworkEvent::ReceivePacket { - packet, - tunnel_info: TunnelInfo::LocalRedirector { - pid, - process_name, - remote_endpoint: None, - }, - }; - if self.net_tx.try_send(event).is_err() { - log::warn!("Dropping incoming packet, TCP channel is full.") - }; - }, - // write packets from the network stack to the IPC pipe to be reinjected. - Some(e) = self.net_rx.recv() => { - match e { - NetworkCommand::SendPacket(packet) => { - let packet = ipc::FromProxy { message: Some(ipc::from_proxy::Message::Packet( ipc::Packet { data: packet.into_inner() }))}; - packet.encode(&mut self.buf.as_mut_slice())?; - let len = packet.encoded_len(); - self.ipc_server.write_all(&self.buf[..len]).await?; - } - } - } - } - } - - log::info!("Windows OS proxy task shutting down."); - Ok(()) - } -} +use std::iter; +use std::os::windows::ffi::OsStrExt; +use std::path::PathBuf; + +use anyhow::{anyhow, Result}; +use tokio::net::windows::named_pipe::{NamedPipeServer, PipeMode, ServerOptions}; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use windows::core::w; +use windows::core::PCWSTR; +use windows::Win32::UI::Shell::ShellExecuteW; +use windows::Win32::UI::Shell::SE_ERR_ACCESSDENIED; +use windows::Win32::UI::WindowsAndMessaging::{SW_HIDE, SW_SHOWNORMAL}; + +use crate::intercept_conf::InterceptConf; +use crate::messages::{TransportCommand, TransportEvent}; +use crate::packet_sources::{forward_packets, PacketSourceConf, PacketSourceTask, IPC_BUF_SIZE}; +use crate::shutdown; + +pub struct WindowsConf { + pub executable_path: PathBuf, +} + +impl PacketSourceConf for WindowsConf { + type Task = WindowsTask; + type Data = UnboundedSender; + + fn name(&self) -> &'static str { + "Windows proxy" + } + + async fn build( + self, + transport_events_tx: Sender, + transport_commands_rx: UnboundedReceiver, + shutdown: shutdown::Receiver, + ) -> Result<(Self::Task, Self::Data)> { + let pipe_name = format!( + r"\\.\pipe\mitmproxy-transparent-proxy-{}", + std::process::id() + ); + + let ipc_server = ServerOptions::new() + .pipe_mode(PipeMode::Message) + .first_pipe_instance(true) + .max_instances(1) + .in_buffer_size(IPC_BUF_SIZE as u32) + .out_buffer_size(IPC_BUF_SIZE as u32) + .reject_remote_clients(true) + .create(&pipe_name)?; + + log::debug!("starting {} {}", self.executable_path.display(), pipe_name); + + let pipe_name = pipe_name + .encode_utf16() + .chain(iter::once(0)) + .collect::>(); + + let executable_path = self + .executable_path + .as_os_str() + .encode_wide() + .chain(iter::once(0)) + .collect::>(); + + let result = unsafe { + ShellExecuteW( + None, + w!("runas"), + PCWSTR::from_raw(executable_path.as_ptr()), + PCWSTR::from_raw(pipe_name.as_ptr()), + None, + if cfg!(debug_assertions) { + SW_SHOWNORMAL + } else { + SW_HIDE + }, + ) + }; + + if cfg!(debug_assertions) { + if result.0 <= 32 { + let err = windows::core::Error::from_win32(); + log::warn!("Failed to start child process: {}", err); + } + } else if result.0 == SE_ERR_ACCESSDENIED as isize { + return Err(anyhow!( + "Failed to start the interception process as administrator." + )); + } else if result.0 <= 32 { + let err = windows::core::Error::from_win32(); + return Err(anyhow!("Failed to start the executable: {}", err)); + } + + let (conf_tx, conf_rx) = unbounded_channel(); + + Ok(( + WindowsTask { + ipc_server, + transport_events_tx, + transport_commands_rx, + conf_rx, + shutdown, + }, + conf_tx, + )) + } +} + +pub struct WindowsTask { + ipc_server: NamedPipeServer, + transport_events_tx: Sender, + transport_commands_rx: UnboundedReceiver, + conf_rx: UnboundedReceiver, + shutdown: shutdown::Receiver, +} + +impl PacketSourceTask for WindowsTask { + async fn run(self) -> Result<()> { + log::debug!("Waiting for IPC connection..."); + self.ipc_server.connect().await?; + log::debug!("IPC connected!"); + + forward_packets( + self.ipc_server, + self.transport_events_tx, + self.transport_commands_rx, + self.conf_rx, + self.shutdown, + ) + .await + } +} diff --git a/src/packet_sources/wireguard.rs b/src/packet_sources/wireguard.rs index 973763f9..5456d711 100755 --- a/src/packet_sources/wireguard.rs +++ b/src/packet_sources/wireguard.rs @@ -2,6 +2,11 @@ use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::sync::Arc; +use crate::messages::{ + NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, +}; +use crate::network::{add_network_layer, MAX_PACKET_SIZE}; +use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; use anyhow::{anyhow, Context, Result}; use boringtun::noise::{ errors::WireGuardError, handshake::parse_handshake_anon, Packet, Tunn, TunnResult, @@ -13,19 +18,13 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::{ net::UdpSocket, sync::{ - broadcast, mpsc::{Receiver, Sender}, Mutex, }, }; -use crate::messages::{ - NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, -}; -use crate::network::{add_network_layer, MAX_PACKET_SIZE}; -use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; - use crate::packet_sources::udp::remote_host_closed_conn; +use crate::shutdown; // WireGuard headers are 60 bytes for IPv4 and 80 bytes for IPv6 const WG_HEADER_SIZE: usize = 80; @@ -55,10 +54,10 @@ impl PacketSourceConf for WireGuardConf { self, transport_events_tx: Sender, transport_commands_rx: UnboundedReceiver, - shutdown: broadcast::Receiver<()>, + shutdown: shutdown::Receiver, ) -> Result<(Self::Task, Self::Data)> { let (network_task_handle, net_tx, net_rx) = - add_network_layer(transport_events_tx, transport_commands_rx, shutdown)?; + add_network_layer(transport_events_tx, transport_commands_rx, shutdown); // initialize WireGuard server let mut peers_by_idx = HashMap::new(); diff --git a/src/shutdown.rs b/src/shutdown.rs index 61190a1a..cd112abc 100755 --- a/src/shutdown.rs +++ b/src/shutdown.rs @@ -1,9 +1,37 @@ use anyhow::Result; +use std::fmt::{Debug, Formatter}; -use tokio::sync::broadcast; +use tokio::sync::watch; use tokio::task::JoinSet; -pub async fn shutdown_task(mut tasks: JoinSet>, shutdown_done: broadcast::Sender<()>) { +#[derive(Clone)] +pub struct Receiver(watch::Receiver<()>); + +impl Receiver { + pub async fn recv(&mut self) { + self.0.changed().await.ok(); + self.0.mark_changed(); + } + + pub fn is_shutting_down(&self) -> bool { + self.0.has_changed().unwrap_or(true) + } +} + +impl Debug for Receiver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Shutdown") + .field(&self.is_shutting_down()) + .finish() + } +} + +pub fn channel() -> (watch::Sender<()>, Receiver) { + let (tx, rx) = watch::channel(()); + (tx, Receiver(rx)) +} + +pub async fn shutdown_task(mut tasks: JoinSet>, shutdown_done: watch::Sender<()>) { while let Some(task) = tasks.join_next().await { match task { Ok(Ok(())) => (), @@ -27,3 +55,24 @@ pub async fn shutdown_task(mut tasks: JoinSet>, shutdown_done: broadc } shutdown_done.send(()).ok(); } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn shutdown_channel() { + let (tx, mut rx1) = channel(); + let rx2 = rx1.clone(); + assert!(!rx1.is_shutting_down()); + assert!(!rx2.is_shutting_down()); + tx.send(()).unwrap(); + rx1.recv().await; + assert!(rx1.is_shutting_down()); + assert!(rx2.is_shutting_down()); + assert!(rx1.is_shutting_down()); + assert!(rx2.is_shutting_down()); + rx1.recv().await; + assert!(rx1.is_shutting_down()); + } +}