pcap-2.2.0/.cargo_vcs_info.json0000644000000001360000000000100117600ustar { "git": { "sha1": "d8bd39e83b818a2d823c26bdafa832d2d92da947" }, "path_in_vcs": "" }pcap-2.2.0/.github/scripts/coverage.py000064400000000000000000000016661046102023000157550ustar 00000000000000import argparse import json import sys def main(coverage_file, fail_under): with open(coverage_file, encoding="utf-8") as f: coverage_json = json.load(f) coverage = float(coverage_json["message"][:-1]) print(f"Code coverage: {coverage:.2f}%; Threshold: {fail_under:.2f}%") success = coverage >= fail_under if coverage < fail_under: print("Insufficient code coverage", file=sys.stderr) return success if __name__ == "__main__": parser = argparse.ArgumentParser(description="Check coverage output by grcov") parser.add_argument("--coverage-file", type=str, required=True, help="Path to the coverage.json file output by grcov") parser.add_argument("--fail-under", type=float, default=100., help="Threshold under which coverage is insufficient") args = parser.parse_args() if not main(args.coverage_file, args.fail_under): exit(2) pcap-2.2.0/.github/workflows/00-linux.yml000064400000000000000000000012261046102023000162450ustar 00000000000000name: 'Linux' on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: "0 6 * * *" jobs: linux-build-and-test-stable: uses: './.github/workflows/01-build-and-test-unix.yml' with: os: 'ubuntu-latest' toolchain: 'stable' linux-build-and-test-msrv: uses: './.github/workflows/01-build-and-test-unix.yml' with: os: 'ubuntu-latest' toolchain: '1.63' msrv: true linux-coverage-stable: uses: './.github/workflows/02-coverage.yml' with: os: 'ubuntu-latest' linux-lint-stable: uses: './.github/workflows/03-lint.yml' with: os: 'ubuntu-latest' pcap-2.2.0/.github/workflows/00-macos.yml000064400000000000000000000012231046102023000162050ustar 00000000000000name: 'Mac OS' on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: "0 6 * * *" jobs: macos-build-and-test-stable: uses: './.github/workflows/01-build-and-test-unix.yml' with: os: 'macos-latest' toolchain: 'stable' macos-build-and-test-msrv: uses: './.github/workflows/01-build-and-test-unix.yml' with: os: 'macos-latest' toolchain: '1.63' msrv: true macos-coverage-stable: uses: './.github/workflows/02-coverage.yml' with: os: 'macos-latest' macos-lint-stable: uses: './.github/workflows/03-lint.yml' with: os: 'macos-latest' pcap-2.2.0/.github/workflows/00-windows.yml000064400000000000000000000020711046102023000165770ustar 00000000000000name: 'Windows' on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: "0 6 * * *" jobs: windows-build-and-test-stable: uses: './.github/workflows/01-build-and-test-windows.yml' with: os: 'windows-latest' toolchain: 'stable' secrets: NPCAP_OEM_PASSWORD: ${{ secrets.NPCAP_OEM_PASSWORD }} NPCAP_OEM_USERNAME: ${{ secrets.NPCAP_OEM_USERNAME }} windows-build-and-test-msrv: uses: './.github/workflows/01-build-and-test-windows.yml' with: os: 'windows-latest' toolchain: '1.63' msrv: true secrets: NPCAP_OEM_PASSWORD: ${{ secrets.NPCAP_OEM_PASSWORD }} NPCAP_OEM_USERNAME: ${{ secrets.NPCAP_OEM_USERNAME }} windows-coverage-stable: uses: './.github/workflows/02-coverage.yml' with: os: 'windows-latest' secrets: NPCAP_OEM_PASSWORD: ${{ secrets.NPCAP_OEM_PASSWORD }} NPCAP_OEM_USERNAME: ${{ secrets.NPCAP_OEM_USERNAME }} windows-lint-stable: uses: './.github/workflows/03-lint.yml' with: os: 'windows-latest' pcap-2.2.0/.github/workflows/01-build-and-test-unix.yml000064400000000000000000000041711046102023000207060ustar 00000000000000on: workflow_call: inputs: os: required: true type: string toolchain: required: true type: string msrv: required: false default: false type: boolean env: RUST_BACKTRACE: 1 CARGO_TERM_VERBOSE: true CARGO_TERM_COLOR: always jobs: build-and-test: runs-on: ${{ inputs.os }} steps: - uses: actions/checkout@v4 - if: ${{ contains(inputs.os, 'ubuntu') }} run: sudo apt-get install libpcap-dev - if: ${{ contains(inputs.os, 'macos') }} run: brew install libpcap - run: | rustup update --no-self-update ${{ inputs.toolchain }} rustup override set ${{ inputs.toolchain }} - if: inputs.msrv run: cp msrv.lock Cargo.lock - name: 'LIBPCAP_VER: 1.0.0' run: cargo build --lib --tests env: LIBPCAP_VER: '1.0.0' - name: 'LIBPCAP_VER: 1.2.1' run: cargo build --lib --tests env: LIBPCAP_VER: '1.2.1' - name: 'LIBPCAP_VER: 1.5.0' run: cargo build --lib --tests env: LIBPCAP_VER: '1.5.0' - name: 'LIBPCAP_VER: 1.5.3' run: cargo build --lib --tests env: LIBPCAP_VER: '1.5.3' - name: 'LIBPCAP_VER: 1.7.2' run: cargo build --lib --tests env: LIBPCAP_VER: '1.7.2' - name: 'LIBPCAP_VER: 1.9.0' run: cargo build --lib --tests env: LIBPCAP_VER: '1.9.0' - name: 'LIBPCAP_VER: 1.9.1' run: cargo build --lib --tests env: LIBPCAP_VER: '1.9.1' - run: cargo build --all-targets - run: cargo test --all-targets - run: cargo build --all-targets --release - run: cargo test --all-targets --release - if: ${{ ! inputs.msrv }} run: cargo build --all-targets --all-features - if: ${{ ! inputs.msrv}} run: cargo test --all-targets --all-features - if: ${{ ! inputs.msrv }} run: cargo build --all-targets --release --all-features - if: ${{ ! inputs.msrv}} run: cargo test --all-targets --release --all-features pcap-2.2.0/.github/workflows/01-build-and-test-windows.yml000064400000000000000000000050661046102023000214210ustar 00000000000000on: workflow_call: inputs: os: required: true type: string toolchain: required: true type: string msrv: required: false default: false type: boolean secrets: NPCAP_OEM_PASSWORD: required: true NPCAP_OEM_USERNAME: required: true env: RUST_BACKTRACE: 1 CARGO_TERM_VERBOSE: true CARGO_TERM_COLOR: always PCAP_CI_TEST_TARGETS: ${{ (github.event_name == 'pull_request') && '--lib' || '--all-targets' }} jobs: build-and-test: runs-on: ${{ inputs.os }} steps: - uses: actions/checkout@v4 - run: | Invoke-WebRequest -Uri "https://npcap.com/dist/npcap-sdk-1.13.zip" -OutFile "C:/npcap-sdk.zip" Expand-Archive -LiteralPath C:/npcap-sdk.zip -DestinationPath C:/npcap-sdk echo "LIB=C:/npcap-sdk/Lib/x64" >> $env:GITHUB_ENV # Secrets are not passed to workflows that are triggered by a pull request from a fork. # https://docs.github.com/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets - if: github.event_name != 'pull_request' run: | $SecPassword = ConvertTo-SecureString "${{ secrets.NPCAP_OEM_PASSWORD }}" -AsPlainText -Force $CredObject = New-Object System.Management.Automation.PSCredential ("${{ secrets.NPCAP_OEM_USERNAME }}", $SecPassword) Invoke-WebRequest -Uri "https://npcap.com/oem/dist/npcap-1.71-oem.exe" -OutFile C:/npcap-oem.exe -Credential $CredObject C:/npcap-oem.exe /S - run: | rustup update --no-self-update ${{ inputs.toolchain }} rustup override set ${{ inputs.toolchain }} - if: inputs.msrv run: cp msrv.lock Cargo.lock # Note that since secrets are not passed to workflows triggered by a pull request from a fork, # it is not possible to run integration tests on pull requests. - run: cargo build --all-targets - run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} - run: cargo build --all-targets --release - run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} --release - if: ${{ ! inputs.msrv }} run: cargo build --all-targets --all-features - if: ${{ ! inputs.msrv}} run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} --all-features - if: ${{ ! inputs.msrv }} run: cargo build --all-targets --release --all-features - if: ${{ ! inputs.msrv}} run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} --release --all-features pcap-2.2.0/.github/workflows/02-coverage.yml000064400000000000000000000116131046102023000167040ustar 00000000000000on: workflow_call: inputs: os: required: true type: string secrets: NPCAP_OEM_PASSWORD: required: false NPCAP_OEM_USERNAME: required: false env: RUST_BACKTRACE: 1 CARGO_TERM_VERBOSE: true CARGO_TERM_COLOR: always PCAP_CI_TEST_TARGETS: ${{ contains(inputs.os, 'windows') && (github.event_name == 'pull_request') && '--lib' || '--all-targets' }} PCAP_CI_GRCOV_CMD: >- ${{ contains(inputs.os, 'windows') && 'C:/grcov' || './grcov' }} target/debug/profraw --binary-path ./target/debug/ --output-types html --source-dir . --ignore-not-existing --ignore "build.rs" --ignore "tests/*" --ignore "examples/*" --excl-start "GRCOV_EXCL_START|mod tests \{" --excl-stop "GRCOV_EXCL_STOP" --output-path ./target/debug/coverage/ PCAP_CI_COV_CMD: >- python3 .github/scripts/coverage.py --coverage-file ./target/debug/coverage/coverage.json PCAP_CI_COV_FAIL_UNDER_NO_FEATURES: ${{ contains(inputs.os, 'windows') && '80.00' || '100.00' }} PCAP_CI_COV_FAIL_UNDER_ALL_FEATURES: ${{ contains(inputs.os, 'windows') && '75.00' || '96.00' }} jobs: coverage: runs-on: ${{ inputs.os }} env: LLVM_PROFILE_FILE: 'target/debug/profraw/pcap-%p-%m.profraw' RUSTFLAGS: '-C instrument-coverage' steps: - uses: actions/checkout@v4 - if: ${{ contains(inputs.os, 'ubuntu') }} run: sudo apt-get install libpcap-dev - if: ${{ contains(inputs.os, 'macos') }} run: brew install libpcap - if: ${{ contains(inputs.os, 'windows') }} run: | Invoke-WebRequest -Uri "https://npcap.com/dist/npcap-sdk-1.13.zip" -OutFile "C:/npcap-sdk.zip" Expand-Archive -LiteralPath C:/npcap-sdk.zip -DestinationPath C:/npcap-sdk echo "LIB=C:/npcap-sdk/Lib/x64" >> $env:GITHUB_ENV - if: ${{ contains(inputs.os, 'windows') && (github.event_name != 'pull_request') }} run: | $SecPassword = ConvertTo-SecureString "${{ secrets.NPCAP_OEM_PASSWORD }}" -AsPlainText -Force $CredObject = New-Object System.Management.Automation.PSCredential ("${{ secrets.NPCAP_OEM_USERNAME }}", $SecPassword) Invoke-WebRequest -Uri "https://npcap.com/oem/dist/npcap-1.71-oem.exe" -OutFile C:/npcap-oem.exe -Credential $CredObject C:/npcap-oem.exe /S # No installation of actuall library since we cannot install OEM pcap on pull request branches # anyway. We'll just be running unit tests on Windows. No integration tests. - run: | rustup update --no-self-update stable rustup default stable rustup component add llvm-tools-preview - if: ${{ contains(inputs.os, 'ubuntu') }} run: >- wget https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-unknown-linux-gnu.tar.bz2 -qO- | tar -xj -C ./ - if: ${{ contains(inputs.os, 'macos') }} run: >- wget https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-apple-darwin.tar.bz2 -qO- | tar -xj -C ./ - if: ${{ contains(inputs.os, 'windows') }} run: | Invoke-WebRequest -Uri "https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-pc-windows-msvc.zip" -OutFile C:/grcov.zip Expand-Archive -LiteralPath C:/grcov.zip -DestinationPath C:/ # Coverage without features. - run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} --no-fail-fast - run: ${{ env.PCAP_CI_GRCOV_CMD }} - run: ${{ env.PCAP_CI_COV_CMD }} --fail-under ${{ env.PCAP_CI_COV_FAIL_UNDER_NO_FEATURES }} # Clean up coverage artifacts. - if: ${{ contains(inputs.os, 'windows') }} run: | Remove-Item -Recurse -Force ./target/debug/coverage Remove-Item -Recurse -Force ./target/debug/profraw - if: ${{ ! contains(inputs.os, 'windows') }} run: rm -rf ./target/debug/{coverage,profraw} - run: cargo clean -p pcap # Coverage with features. - run: cargo test ${{ env.PCAP_CI_TEST_TARGETS }} --all-features --no-fail-fast - run: ${{ env.PCAP_CI_GRCOV_CMD }} - run: ${{ env.PCAP_CI_COV_CMD }} --fail-under ${{ env.PCAP_CI_COV_FAIL_UNDER_ALL_FEATURES }} # On push to main push results to coverage branch. - if: ${{ (github.event_name == 'push') && (github.ref == 'refs/heads/main') && contains(inputs.os, 'ubuntu') }} uses: actions/checkout@v4 with: clean: false ref: coverage - if: ${{ (github.event_name == 'push') && (github.ref == 'refs/heads/main') && contains(inputs.os, 'ubuntu') }} run: | rm -rf *.html badges src coverage.json cp -r ./target/debug/coverage/* ./ git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add -A git commit -m "Code coverage for "$(git rev-parse --short origin/main) git push pcap-2.2.0/.github/workflows/03-lint.yml000064400000000000000000000023511046102023000160570ustar 00000000000000on: workflow_call: inputs: os: required: true type: string env: RUST_BACKTRACE: 1 CARGO_TERM_VERBOSE: true CARGO_TERM_COLOR: always jobs: lint: runs-on: ${{ inputs.os }} env: PCAP_CI_CLIPPY_FLAGS: '-D warnings' steps: - uses: actions/checkout@v4 - run: | rustup update --no-self-update stable rustup override set stable - run: cargo clippy --all-targets -- ${{ env.PCAP_CI_CLIPPY_FLAGS }} - run: cargo clippy --all-targets --release -- ${{ env.PCAP_CI_CLIPPY_FLAGS }} - run: cargo clippy --all-targets --all-features -- ${{ env.PCAP_CI_CLIPPY_FLAGS }} - run: cargo clippy --all-targets --release --all-features -- ${{ env.PCAP_CI_CLIPPY_FLAGS }} - run: cargo doc --no-deps --release env: DOCS_RS: true - run: cargo doc --no-deps --release --all-features env: DOCS_RS: true - uses: obi1kenobi/cargo-semver-checks-action@v2 with: feature-group: "all-features" env: # Cargo semver uses rust docs. We set DOCS_RS to make sure the same docs are built. DOCS_RS: true - run: cargo fmt -- --check pcap-2.2.0/.gitignore000064400000000000000000000000221046102023000125320ustar 00000000000000target Cargo.lock pcap-2.2.0/CHANGELOG.md000064400000000000000000000104721046102023000123650ustar 00000000000000# Changelog ## [Unreleased] ## [2.2.0] - 2024-09-01 ### Added - Added an implementation of `AsFd` on `Capture` on non-Windows. ## [2.1.0] - 2024-08-27 ### Added - Add `want_pktap` on `Capture` for Mac OS. ## [2.0.0] - 2024-04-21 ### Changed - Rust Edition is now `2021`. - MSRV is now `1.63.0`. ## [1.3.0] - 2024-03-15 ### Added - Binding for `pcap_loop` added. It can be accessed via the `for_each` call on Activated captures. ## [1.2.0] - 2024-01-19 ### Added - `capture-stream` support added for Windows. ## [1.1.0] - 2023-05-12 ### Added - `lending-iter` UNSTABLE feature that introduces lending iterator using GATs. ### Changed - Examples in the docs have been fixed. ## [1.0.0] - 2022-11-19 ### Changed - Public API declared stable. ## [0.11.0] - 2022-10-01 ### Added - `SendQueue::queue_sg()` can add scattered packets, as a slice of `std::io::IoSlice`s, to `SendQueue`s. ### Changed - Rename `sendqueue::Sync` to `sendqueue::SendSync` to avoid collision with `Sync` in std's prelude. - Build script will fall back to `pkg-config` if available and `LIBPCAP_LIBDIR` hasn't been explicitly set. ## [0.10.1] - 2022-08-17 ### Changed - MSRV is now `1.46.0`. ## [0.10.0] - 2022-08-16 ### Added - [doc](https://docs.rs/pcap/latest/pcap/) will now include all features. - Support for sendqueues on Windows. - `PacketStream::capture_mut` to still be able to inject packets when using `PacketStream`. - `Capture::iter()` that return an iterator that use a codec like `Capture::stream()`. - `Packet::dead_with_precision` to enable creating a pcap with nanosecond precision. - `flags` field to `Device`. ### Removed - `BpfProgram` no longer have `Clone` implementation see [#261](https://github.com/rust-pcap/pcap/issues/261) ### Changed - MSRV is now `1.41.0`. - `PacketStream` has been moved from mod `stream` to the `root` of the crate. - `PacketCodec` has been moved from mod `stream` to the `root` of the crate. - `PacketCodec::decode()` no longer returns a `Result`. - `PacketCodec::Type` has been renamed to `PacketCodec::Item`. - `Device::lookup` now returns `Result, Error>` rather than `Result`. `Ok(None)` means that the lookup succeeded, but no suitable devices were available. This is consistent with libpcap. - `Capture` and `Savefile` no longer implement the `Sync` trait. The underlying `libpcap` library does not promise thread-safe access for the same capture object from multiple threads. - Switched from `winapi` to `windows-sys` for Windows builds. `windows-sys` requires rustc 1.46.0. - `Capture::next` have been rename `next_packet` to avoid any confusion with `Iterator::next`. ### Removed - mod `stream` is no longer public. - `docs-rs` feature. - `full` feature. - `stream::SelectableFd` and `stream::PacketStream::new` as they were only meant to be used internally. ## [0.9.2] - 2022-04-15 ### Changed - `capture-stream` requires rustc version 1.49.0 due to dependency on `tokio`. ## [0.9.1] - 2021-11-07 ### Added - Support for device addresses. ## [0.9.0] - 2021-09-05 ### Added - `savefile.flush` support. ### Changed - Updated dependency `tokio` from version 0.2 to 1.0. - `capture-stream` requires rustc version 1.45.0 due to dependency on `tokio`. ## [0.8.1] - 2020-12-30 ### Changed - Fix docs.rs build. ## [0.8.0] - 2020-12-30 ### Added - `Derive(Clone)` to `Device` struct (#100). - Build-time `libpcap` version detection. - Support for immediate mode. - Const value for Linktype (#145). - Support for BPF compile. ### Changed - Opt into Rust 2018. - Now minimum supported rustc version is 1.40.0. - Updated dependency from deprecated `tokio-core` to `tokio` 0.2. - Updated dependency `futures` from version 0.1 to 0.3. - Feature `tokio` renamed to `capture-stream` because Cargo does not allow features and dependencies to have the same name. - `PCAP_LIBDIR` renamed to `LIBPCAP_LIBDIR` to distinguish the `pcap` crate from the `libpcap` library. - All methods that construct objects out of a `RawFd` are now unsafe. - All methods that take a raw pointer are now unsafe. Some of these functions were renamed from `new` to `from_handle` to underline this. ### Removed - Feature flags `pcap-savefile-append`, `pcap-fopen-offline-precision` (replaced by build-time `libpcap` version detection). ## [0.7.0] - 2017-08-04 No Changelog entries for <= 0.7.0. pcap-2.2.0/CONTRIBUTING.md000064400000000000000000000037211046102023000130040ustar 00000000000000# Contributing ## Code coverage The current code coverage for the `main` branch is automatically published on every push to `main` on https://rust-pcap.github.io/pcap/. ### Pre-requisites To obtain code coverage locally you will need [compatible LLVM coverage tools](https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools) and [`grcov`](https://github.com/mozilla/grcov). The easiest way to obtain compatible LLVM coverage tools is by adding the `llvm-tools-preview` `rustup` component (for nightly!): ``` rustup component add llvm-tools-preview ``` `grcov` can be installed through cargo: ``` cargo install grcov ``` ### Obtaining code coverage Clean any previously compiled objects and binaries. ``` rm -rf ./target/debug/{coverage,profraw} cargo clean -p pcap ``` Compile and run the tests. We set `RUSTFLAGS="-C instrument-coverage"` to enable source-based code coverage and `LLVM_PROFILE_FILE="target/debug/coverage/profraw/pcap-%p-%m.profraw"` to make sure each test gets its own profile information. ``` env RUSTFLAGS="-C instrument-coverage" \ LLVM_PROFILE_FILE="target/debug/profraw/pcap-%p-%m.profraw" \ cargo test --all-features --all-targets ``` And finally, run `grcov` to obtain a report. ``` grcov target/debug/profraw \ --binary-path ./target/debug/ \ --output-types html \ --source-dir . \ --ignore-not-existing \ --ignore "build.rs" \ --ignore "tests/*" \ --ignore "examples/*" \ --excl-start "GRCOV_EXCL_START|mod tests \{" \ --excl-stop "GRCOV_EXCL_STOP" \ --output-path ./target/debug/coverage/ ``` The code coverage report will be available in `target/debug/coverage/index.html` which you can explore in your browser. ``` xdg-open target/debug/coverage/index.html ``` ### More information For more information on LLVM code coverage in Rust: https://doc.rust-lang.org/rustc/instrument-coverage.html For more information on `grcov`: https://github.com/mozilla/grcov pcap-2.2.0/Cargo.lock0000644000001064760000000000100077510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ "byteorder", "iovec", ] [[package]] name = "cc" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "crossbeam-deque" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" dependencies = [ "crossbeam-epoch", "crossbeam-utils", "maybe-uninit", ] [[package]] name = "crossbeam-epoch" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg", "cfg-if 0.1.10", "crossbeam-utils", "lazy_static", "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-queue" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ "cfg-if 0.1.10", "crossbeam-utils", "maybe-uninit", ] [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", "cfg-if 0.1.10", "lazy_static", ] [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "downcast" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "errno" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ "errno-dragonfly", "libc", "winapi 0.3.9", ] [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "etherparse" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "827292ea592108849932ad8e30218f8b1f21c0dfd0696698a18b5d0aed62d990" dependencies = [ "arrayvec", ] [[package]] name = "eui48" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "887418ac5e8d57c2e66e04bdc2fe15f9a5407be20b54a82c86bd0e368b709701" dependencies = [ "regex", "rustc-serialize", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fragile" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ "bitflags 1.3.2", "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gat-std" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08eebf4c7afb1dbb5a68ece983498cf17a788cc076321d9fcc8a3f86da36493" dependencies = [ "gat-std-proc", ] [[package]] name = "gat-std-proc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c20aeb660d16a16f6611d5be552a02b68edd5c046574d03d2e791e7446a262f" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ "libc", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", "windows-targets 0.52.5", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", "kernel32-sys", "libc", "log", "miow", "net2", "slab", "winapi 0.2.8", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "mio-uds" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", "mio 0.6.23", ] [[package]] name = "miow" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", "winapi 0.2.8", "ws2_32-sys", ] [[package]] name = "mockall" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if 1.0.0", "downcast", "fragile", "lazy_static", "mockall_derive", "predicates", "predicates-tree", ] [[package]] name = "mockall_derive" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "net2" version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ "lock_api", "parking_lot_core", "rustc_version", ] [[package]] name = "parking_lot_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" dependencies = [ "cfg-if 0.1.10", "cloudabi", "libc", "redox_syscall", "rustc_version", "smallvec", "winapi 0.3.9", ] [[package]] name = "pcap" version = "2.2.0" dependencies = [ "bitflags 1.3.2", "errno 0.2.8", "etherparse", "eui48", "futures 0.3.30", "gat-std", "libc", "libloading", "mockall", "once_cell", "pkg-config", "regex", "tempfile", "tokio 1.37.0", "tun-tap", "windows-sys 0.36.1", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "predicates" version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", "itertools", "normalize-line-endings", "predicates-core", "regex", ] [[package]] name = "predicates-core" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" [[package]] name = "predicates-tree" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-serialize" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ "semver", ] [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno 0.3.9", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "scoped-tls" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ "maybe-uninit", ] [[package]] name = "socket2" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "termtree" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "tokio" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes", "futures 0.1.31", "mio 0.6.23", "num_cpus", "tokio-codec", "tokio-current-thread", "tokio-executor", "tokio-fs", "tokio-io", "tokio-reactor", "tokio-sync", "tokio-tcp", "tokio-threadpool", "tokio-timer", "tokio-udp", "tokio-uds", ] [[package]] name = "tokio" version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "libc", "mio 0.8.11", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-codec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes", "futures 0.1.31", "tokio-io", ] [[package]] name = "tokio-core" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" dependencies = [ "bytes", "futures 0.1.31", "iovec", "log", "mio 0.6.23", "scoped-tls", "tokio 0.1.22", "tokio-executor", "tokio-io", "tokio-reactor", "tokio-timer", ] [[package]] name = "tokio-current-thread" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ "futures 0.1.31", "tokio-executor", ] [[package]] name = "tokio-executor" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils", "futures 0.1.31", ] [[package]] name = "tokio-fs" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ "futures 0.1.31", "tokio-io", "tokio-threadpool", ] [[package]] name = "tokio-io" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes", "futures 0.1.31", "log", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "tokio-reactor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils", "futures 0.1.31", "lazy_static", "log", "mio 0.6.23", "num_cpus", "parking_lot", "slab", "tokio-executor", "tokio-io", "tokio-sync", ] [[package]] name = "tokio-sync" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", "futures 0.1.31", ] [[package]] name = "tokio-tcp" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes", "futures 0.1.31", "iovec", "mio 0.6.23", "tokio-io", "tokio-reactor", ] [[package]] name = "tokio-threadpool" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils", "futures 0.1.31", "lazy_static", "log", "num_cpus", "slab", "tokio-executor", ] [[package]] name = "tokio-timer" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils", "futures 0.1.31", "slab", "tokio-executor", ] [[package]] name = "tokio-udp" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes", "futures 0.1.31", "log", "mio 0.6.23", "tokio-codec", "tokio-io", "tokio-reactor", ] [[package]] name = "tokio-uds" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes", "futures 0.1.31", "iovec", "libc", "log", "mio 0.6.23", "mio-uds", "tokio-codec", "tokio-io", "tokio-reactor", ] [[package]] name = "tun-tap" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a477a4e9367c72ac875d23cd07ac99ffa932497d8428767fed0cfa27bbabe50" dependencies = [ "cc", "futures 0.1.31", "libc", "mio 0.6.23", "tokio-core", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc 0.36.1", "windows_i686_gnu 0.36.1", "windows_i686_msvc 0.36.1", "windows_x86_64_gnu 0.36.1", "windows_x86_64_msvc 0.36.1", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ "winapi 0.2.8", "winapi-build", ] pcap-2.2.0/Cargo.toml0000644000000067450000000000100077720ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63" name = "pcap" version = "2.2.0" authors = [ "Sean Bowe ", "Wojciech Kozlowski ", "Hideki Sekine", ] build = "build.rs" autobins = false autoexamples = false autotests = false autobenches = false description = "A packet capture API around pcap/wpcap" homepage = "https://github.com/rust-pcap/pcap" documentation = "https://docs.rs/pcap" readme = "README.md" keywords = [ "pcap", "packet", "sniffing", ] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-pcap/pcap" [package.metadata.docs.rs] all-features = true [lib] name = "pcap" path = "src/lib.rs" [[example]] name = "easylisten" path = "examples/easylisten.rs" [[example]] name = "getdevices" path = "examples/getdevices.rs" [[example]] name = "getstatistics" path = "examples/getstatistics.rs" [[example]] name = "iterprint" path = "examples/iterprint.rs" [[example]] name = "lendingiterprint" path = "examples/lendingiterprint.rs" required-features = ["lending-iter"] [[example]] name = "listenlocalhost" path = "examples/listenlocalhost.rs" [[example]] name = "loop" path = "examples/loop.rs" [[example]] name = "nfbpfcompile" path = "examples/nfbpfcompile.rs" [[example]] name = "savefile" path = "examples/savefile.rs" [[example]] name = "savemultiplefiles" path = "examples/savemultiplefiles.rs" [[example]] name = "sendqueue" path = "examples/sendqueue.rs" [[example]] name = "stdin" path = "examples/stdin.rs" [[example]] name = "streamecho" path = "examples/streamecho.rs" required-features = ["capture-stream"] [[example]] name = "streamlisten" path = "examples/streamlisten.rs" required-features = ["capture-stream"] [[example]] name = "streamlisten_mt" path = "examples/streamlisten_mt.rs" required-features = ["capture-stream"] [[test]] name = "lib" path = "tests/lib.rs" [[test]] name = "tap_tests" path = "tests/tap_tests.rs" [dependencies.bitflags] version = "1.3" [dependencies.errno] version = "0.2" [dependencies.futures] version = "0.3" optional = true [dependencies.gat-std] version = "0.1.1" optional = true [dependencies.libc] version = "0.2" [dependencies.tokio] version = "1.0" features = [ "net", "rt", "macros", "rt-multi-thread", ] optional = true [dev-dependencies.etherparse] version = "0.13.0" [dev-dependencies.mockall] version = "0.11.4" [dev-dependencies.once_cell] version = "1.14.0" [dev-dependencies.tempfile] version = "3.10" [build-dependencies.libloading] version = "0.8" [build-dependencies.pkg-config] version = "0.3" [build-dependencies.regex] version = "1" [features] capture-stream = [ "tokio", "futures", "windows-sys/Win32_System_Threading", ] lending-iter = ["gat-std"] [target.'cfg(not(target_os = "windows"))'.dev-dependencies.tun-tap] version = "0.1.3" [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.36.1" features = [ "Win32_Foundation", "Win32_Networking_WinSock", ] [target.'cfg(target_os = "windows")'.dev-dependencies.eui48] version = "1.1" pcap-2.2.0/Cargo.toml.orig000064400000000000000000000047761046102023000134550ustar 00000000000000[package] name = "pcap" version = "2.2.0" authors = ["Sean Bowe ", "Wojciech Kozlowski ", "Hideki Sekine"] edition = "2021" rust-version = "1.63" description = "A packet capture API around pcap/wpcap" keywords = ["pcap", "packet", "sniffing"] readme = "README.md" homepage = "https://github.com/rust-pcap/pcap" repository = "https://github.com/rust-pcap/pcap" documentation = "https://docs.rs/pcap" license = "MIT OR Apache-2.0" build = "build.rs" [dependencies] bitflags = "1.3" libc = "0.2" errno = "0.2" tokio = { version = "1.0", features = ["net", "rt", "macros", "rt-multi-thread"], optional = true } futures = { version = "0.3", optional = true } gat-std = { version = "0.1.1", optional = true } [target.'cfg(target_os = "windows")'.dependencies] windows-sys = { version = "0.36.1", features = ["Win32_Foundation", "Win32_Networking_WinSock"] } [dev-dependencies] etherparse = "0.13.0" once_cell = "1.14.0" mockall = "0.11.4" tempfile = "3.10" [target.'cfg(target_os = "windows")'.dev-dependencies] eui48 = "1.1" [target.'cfg(not(target_os = "windows"))'.dev-dependencies] tun-tap = "0.1.3" [build-dependencies] libloading = "0.8" regex = "1" pkg-config = "0.3" [features] # This feature enables access to the function Capture::stream. # This is disabled by default, because it depends on a tokio capture-stream = ["tokio", "futures", "windows-sys/Win32_System_Threading"] lending-iter = ["gat-std"] [lib] name = "pcap" [[example]] name = "easylisten" path = "examples/easylisten.rs" [[example]] name = "getdevices" path = "examples/getdevices.rs" [[example]] name = "getstatistics" path = "examples/getstatistics.rs" [[example]] name = "iterprint" path = "examples/iterprint.rs" [[example]] name = "lendingiterprint" path = "examples/lendingiterprint.rs" required-features = ["lending-iter"] [[example]] name = "listenlocalhost" path = "examples/listenlocalhost.rs" [[example]] name = "nfbpfcompile" path = "examples/nfbpfcompile.rs" [[example]] name = "savefile" path = "examples/savefile.rs" [[example]] name = "sendqueue" path = "examples/sendqueue.rs" [[example]] name = "stdin" path = "examples/stdin.rs" [[example]] name = "streamecho" path = "examples/streamecho.rs" required-features = ["capture-stream"] [[example]] name = "streamlisten" path = "examples/streamlisten.rs" required-features = ["capture-stream"] [[example]] name = "streamlisten_mt" path = "examples/streamlisten_mt.rs" required-features = ["capture-stream"] [package.metadata.docs.rs] all-features = true pcap-2.2.0/LICENSE-APACHE000064400000000000000000000261361046102023000125040ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pcap-2.2.0/LICENSE-MIT000064400000000000000000000020471046102023000122070ustar 00000000000000Copyright (c) 2015 The pcap Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pcap-2.2.0/README.md000064400000000000000000000150561046102023000120360ustar 00000000000000# pcap This is a **Rust language** crate for accessing the packet sniffing capabilities of libpcap (or Npcap on Windows). If you need anything, feel free to post an issue or submit a pull request! [![Linux](https://github.com/rust-pcap/pcap/actions/workflows/00-linux.yml/badge.svg)](https://github.com/rust-pcap/pcap/actions/workflows/00-linux.yml) [![Mac OS](https://github.com/rust-pcap/pcap/actions/workflows/00-macos.yml/badge.svg)](https://github.com/rust-pcap/pcap/actions/workflows/00-macos.yml) [![Windows](https://github.com/rust-pcap/pcap/actions/workflows/00-windows.yml/badge.svg)](https://github.com/rust-pcap/pcap/actions/workflows/00-windows.yml) [![Coverage](https://rust-pcap.github.io/pcap/badges/flat.svg)](https://rust-pcap.github.io/pcap/index.html) [![Crates.io](https://img.shields.io/crates/v/pcap.svg)](https://crates.io/crates/pcap) [![Docs.rs](https://docs.rs/pcap/badge.svg)](https://docs.rs/pcap) ## Features: * List devices * Open capture handle on a device or savefiles * Get packets from the capture handle * Filter packets using BPF programs * List/set/get datalink link types * Configure some parameters like promiscuity and buffer length * Write packets to savefiles * Inject packets into an interface See [examples](examples) for usage. # Building This crate requires the libpcap (or Npcap on Windows) library. ## Installing dependencies ### Windows 1. Install [Npcap](https://npcap.com/#download). 2. Download the [Npcap SDK](https://npcap.com/#download). 3. Add the SDK's `/Lib` or `/Lib/x64` folder to your `LIB` environment variable. ### Linux Install the libraries and header files for the libpcap library. For example: - On Debian based Linux: install `libpcap-dev`. - On Fedora Linux: install `libpcap-devel`. **Note:** If not running as root, you need to set capabilities like so: `sudo setcap cap_net_raw,cap_net_admin=eip path/to/bin`. ### Mac OS X `libpcap` should be installed on Mac OS X by default. **Note:** A timeout of zero may cause ```pcap::Capture::next``` to hang and never return (because it waits for the timeout to expire before returning). This can be fixed by using a non-zero timeout (as the libpcap manual recommends) and calling ```pcap::Capture::next``` in a loop. ## Linking It is your responsibility, as the crate user, to configure linking with libpcap/wpcap to suit your needs (e.g. library version, static vs. dynamic linking, etc.) via your own [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html). For most setups, the defaults are most likely sufficient and you don't have to do anything special beyond installing libpcap as described above. The notes below are provided if the defaults are not suitable. ### Supporting different library versions This crate supports several different versions of libpcap, such as wpcap, to ensure it can be compiled against older versions while still providing access to functionality available in newer versions. The build script will try to automatically detect the right version and configure pcap, but it may fail at this task. Especially, if you have an unusual build setup. If you are getting compilation error of the form ``` text cannot find function `pcap_` in module `raw` ``` then that is probably what is happening. It is likely that your libpcap does not support the newest libpcap API and pcap failed to query libpcap to find out which unsupported features it should exclude. To solve this, you can try helping the pcap crate compile the correct feature set that is compatible with your libpcap using the following two approaches: #### Library Location If you are linking dynamically with libpcap, pcap will try to consult libpcap for its version. However, if your library is in an unconventional location and you had to customize `cargo:rustc-link-search=native` in your own build script, pcap's build script is unable to pick up on that and will default to the most recent API version. If you are not using the most recent library version, please communicate the library's location to pcap's build script using the `LIBPCAP_LIBDIR` environment variable. If `LIBPCAP_LIBDIR` is unset, the build will attempt to find the library via `pkg-config` instead. On most setups, this is the easiest way to get things working and may even eliminate the need for any custom build scripts in your software. #### Library Version If setting the library location does not work or you are linking statically, you may need to set the libpcap version manually. You can do this by setting the environment variable `LIBPCAP_VER` to the desired version (e.g. `env LIBPCAP_VER=1.5.0`). By default, if pcap fails to query libpcap/wpcap for its API version, it will assume the newest API so this should only be necessary if you are using an old version of libpcap. Note that `LIBPCAP_VER` is respected even if you haven't set `LIBPCAP_LIBDIR` and are using `pkg-config`. If it is unset, we'll find whatever available version as long as it's supported by the library. ## Optional Features ### `capture-stream` Use the `capture-stream` feature to enable support for streamed packet captures. ```toml [dependencies] pcap = { version = "2", features = ["capture-stream"] } ``` ## Unstable Features Use at your own risk, we do not consider this our public API yet. ### `lending-iter` Use the `lending-iter` feature to enable the lending packet iterator. See `lendingiterprint` example. ## Minimum Supported Rust Version (MSRV) This crate uses Rust 2021 and requires a compiler version >= 1.63. The feature `capture-stream` depends on `tokio = "1.0"`. Therefore, when `capture-stream` is enabled, this crate requires a compiler version new enough to compile the `tokio` crate. Some dependencies no longer support our chosen MSRV. Since many crates do not consider this a breaking change there is not much that can be done to prevent this through semver requirements. However, users can protect themselves against such incompatibility with `Cargo.lock` file. We provide [`msrv.lock`](msrv.lock) which is the lockfile against which we test MSRV builds in our CI. [Discuss the MSRV](https://github.com/rust-pcap/pcap/discussions/240). # License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. # Contributing Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. pcap-2.2.0/build.rs000064400000000000000000000144641046102023000122260ustar 00000000000000use std::env; use std::ffi::CStr; use std::os::raw::c_char; use std::path::PathBuf; #[derive(PartialEq, Eq, PartialOrd, Ord)] struct Version { major: usize, minor: usize, micro: usize, } impl Version { const LOWEST_SUPPORTED: Self = Self { major: 1, minor: 0, micro: 0, }; fn new(major: usize, minor: usize, micro: usize) -> Version { Version { major, minor, micro, } } fn list() -> Vec { vec![ Version::new(1, 2, 1), Version::new(1, 5, 0), Version::new(1, 5, 3), Version::new(1, 7, 2), Version::new(1, 9, 0), Version::new(1, 9, 1), ] } fn max() -> Version { #[cfg(not(windows))] { Version::new(1, 9, 1) } #[cfg(windows)] { Version::new(1, 0, 0) } } fn docs_rs() -> Version { Version::new(2, 0, 0) } fn parse(s: &str) -> Result> { let err = format!("invalid pcap lib version: {}", s); let re = regex::Regex::new(r"([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)")?; let captures = re.captures(s).ok_or_else(|| err.clone())?; let major_str = captures.get(1).ok_or_else(|| err.clone())?.as_str(); let minor_str = captures.get(2).ok_or_else(|| err.clone())?.as_str(); let micro_str = captures.get(3).ok_or_else(|| err.clone())?.as_str(); Ok(Version::new( major_str.parse::()?, minor_str.parse::()?, micro_str.parse::()?, )) } } impl std::fmt::Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Version { major, minor, micro, } = self; write!(f, "{}.{}.{}", major, minor, micro) } } fn get_libpcap_version(libdirpath: Option) -> Result> { if std::env::var("DOCS_RS").is_ok() { return Ok(Version::docs_rs()); } if let Ok(libver) = env::var("LIBPCAP_VER") { return Version::parse(&libver); } #[cfg(all(unix, not(target_os = "macos")))] let mut libfile = PathBuf::from("libpcap.so"); #[cfg(target_os = "macos")] let mut libfile = PathBuf::from("libpcap.dylib"); #[cfg(windows)] let mut libfile = PathBuf::from("wpcap.dll"); if let Some(libdir) = libdirpath { libfile = libdir.join(libfile); } let lib = if let Ok(lib) = unsafe { // Loading a shared library is unsafe in the general case since initialization and // termination routines could have arbitrary behavior. This is sound as long as the shared // library is "well-behaved." See https://github.com/nagisa/rust_libloading/issues/86 and // https://github.com/nagisa/rust_libloading/blob/0.8.3/src/changelog.rs#L96-L151 for // details. libloading::Library::new(libfile) } { lib } else { return Ok(Version::max()); }; type PcapLibVersion = unsafe extern "C" fn() -> *mut c_char; let pcap_lib_version = unsafe { lib.get::(b"pcap_lib_version")? }; let c_buf: *const c_char = unsafe { pcap_lib_version() }; let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; let v_str: &str = c_str.to_str()?; let err = format!("cannot infer pcap lib version from: {}", v_str); #[cfg(not(windows))] { let re = regex::Regex::new(r"libpcap version ([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)")?; let captures = re.captures(v_str).ok_or_else(|| err.clone())?; let major_str = captures.get(1).ok_or_else(|| err.clone())?.as_str(); let minor_str = captures.get(2).ok_or_else(|| err.clone())?.as_str(); let micro_str = captures.get(3).ok_or_else(|| err.clone())?.as_str(); Ok(Version::new( major_str.parse::()?, minor_str.parse::()?, micro_str.parse::()?, )) } #[cfg(windows)] { let re = regex::Regex::new(r"based on libpcap version ([[:digit:]]+)\.([[:digit:]]+)")?; let captures = re.captures(v_str).ok_or_else(|| err.clone())?; let major_str = captures.get(1).ok_or_else(|| err.clone())?.as_str(); let minor_str = captures.get(2).ok_or_else(|| err.clone())?.as_str(); Ok(Version::new( major_str.parse::()?, minor_str.parse::()?, 0, )) } } fn emit_cfg_flags(version: Version) { assert!( version >= Version::LOWEST_SUPPORTED, "required pcap lib version: >=1.0.0" ); for v in Version::list().iter() { println!( "cargo:rustc-check-cfg=cfg(libpcap_{}_{}_{})", v.major, v.minor, v.micro ); if v <= &version { println!( "cargo:rustc-cfg=libpcap_{}_{}_{}", v.major, v.minor, v.micro ); } } } fn main() { println!("cargo:rerun-if-env-changed=LIBPCAP_LIBDIR"); println!("cargo:rerun-if-env-changed=LIBPCAP_VER"); // If user explicitly set LIBPCAP_LIBDIR, honour their wishes. This keeps // existing build scripts running. If it's not set, try pkg-config. If // that's not set, try last ditch effort to build even though library wasn't // explicitly given. let version = if let Ok(libdir) = env::var("LIBPCAP_LIBDIR") { println!("cargo:rustc-link-search=native={}", libdir); get_libpcap_version(Some(PathBuf::from(&libdir))).unwrap() } else if let Ok(library) = from_pkg_config() { Version::parse(&library.version).unwrap() } else { get_libpcap_version(None).unwrap() }; emit_cfg_flags(version); } fn from_pkg_config() -> Result { let mut config = pkg_config::Config::new(); // If the user has went out of their way to specify LIBPCAP_VER (even though // LIBCAP_LIBDIR wasn't set), respect it. Otherwise fall back to any version // as long as it's supported. if let Ok(v) = env::var("LIBPCAP_VER") { config.exactly_version(&v); } else { config.atleast_version(&Version::LOWEST_SUPPORTED.to_string()); }; config.probe("libpcap") } pcap-2.2.0/examples/easylisten.rs000064400000000000000000000007021046102023000151130ustar 00000000000000fn main() { // get the default Device let device = pcap::Device::lookup() .expect("device lookup failed") .expect("no device available"); println!("Using device {}", device.name); // Setup Capture let mut cap = pcap::Capture::from_device(device) .unwrap() .immediate_mode(true) .open() .unwrap(); // get a packet and print its bytes println!("{:?}", cap.next_packet()); } pcap-2.2.0/examples/getdevices.rs000064400000000000000000000004731046102023000150620ustar 00000000000000fn main() { // list all of the devices pcap tells us are available for device in pcap::Device::list().expect("device lookup failed") { println!("Found device! {:?}", device); // now you can create a Capture with this Device if you want. // see example/easylisten.rs for how } } pcap-2.2.0/examples/getstatistics.rs000064400000000000000000000011521046102023000156250ustar 00000000000000fn main() { // get the default Device let device = pcap::Device::lookup() .expect("device lookup failed") .expect("no device available"); println!("Using device {}", device.name); // Setup Capture let mut cap = pcap::Capture::from_device(device) .unwrap() .immediate_mode(true) .open() .unwrap(); // get 10 packets for _ in 0..10 { cap.next_packet().ok(); } let stats = cap.stats().unwrap(); println!( "Received: {}, dropped: {}, if_dropped: {}", stats.received, stats.dropped, stats.if_dropped ); } pcap-2.2.0/examples/iterprint.rs000064400000000000000000000017361046102023000147630ustar 00000000000000//! Example of using iterators that print paquet use pcap::{Capture, Device, Packet, PacketCodec, PacketHeader}; use std::error; /// Represents a owned packet #[derive(Debug, Clone, PartialEq, Eq)] pub struct PacketOwned { pub header: PacketHeader, pub data: Box<[u8]>, } /// Simple codec that tranform [`pcap::Packet`] into [`PacketOwned`] pub struct Codec; impl PacketCodec for Codec { type Item = PacketOwned; fn decode(&mut self, packet: Packet) -> Self::Item { PacketOwned { header: *packet.header, data: packet.data.into(), } } } fn main() -> Result<(), Box> { let device = Device::lookup()?.ok_or("no device available")?; // get the default Device println!("Using device {}", device.name); let cap = Capture::from_device(device)?.immediate_mode(true).open()?; for packet in cap.iter(Codec) { let packet = packet?; println!("{:?}", packet); } Ok(()) } pcap-2.2.0/examples/lendingiterprint.rs000064400000000000000000000010001046102023000163040ustar 00000000000000//! Example of using lending iterators that print paquet use pcap::{Capture, Device}; use std::error; use gat_std::gatify; #[gatify] fn main() -> Result<(), Box> { let device = Device::lookup()?.ok_or("no device available")?; // get the default Device println!("Using device {}", device.name); let cap = Capture::from_device(device)?.immediate_mode(true).open()?; for packet in cap { let packet = packet?; println!("{:?}", packet); } Ok(()) } pcap-2.2.0/examples/listenlocalhost.rs000064400000000000000000000007721046102023000161510ustar 00000000000000fn main() { // listen on the device named "any", which is only available on Linux. This is only for // demonstration purposes. let mut cap = pcap::Capture::from_device("any") .unwrap() .immediate_mode(true) .open() .unwrap(); // filter out all packets that don't have 127.0.0.1 as a source or destination. cap.filter("host 127.0.0.1", true).unwrap(); while let Ok(packet) = cap.next_packet() { println!("got packet! {:?}", packet); } } pcap-2.2.0/examples/loop.rs000064400000000000000000000010761046102023000137110ustar 00000000000000fn main() { // get the default Device let device = pcap::Device::lookup() .expect("device lookup failed") .expect("no device available"); println!("Using device {}", device.name); // Setup Capture let mut cap = pcap::Capture::from_device(device) .unwrap() .immediate_mode(true) .open() .unwrap(); let mut count = 0; cap.for_each(None, |packet| { println!("Got {:?}", packet.header); count += 1; if count > 100 { panic!("ow"); } }) .unwrap(); } pcap-2.2.0/examples/nfbpfcompile.rs000064400000000000000000000031111046102023000153740ustar 00000000000000//! nfbpf_compile works the same way as the small tool bundled with iptables: //! it compiles a pcap expression in to a BPF filter, then serializes it using //! a simple, safe encoding. use pcap::{BpfProgram, Capture, Linktype}; use std::env; use std::process; fn main() { let (layertype, prog) = match env::args().len() { 2 => ("RAW".to_string(), env::args().nth(1).unwrap()), 3 => (env::args().nth(1).unwrap(), env::args().nth(2).unwrap()), _ => { println!("Usage: {} [type] 'program'", env::args().next().unwrap()); println!(" type: a pcap linklayer type, e.g:"); println!(" RAW, EN10MB"); println!(" program: a pcap filter expression e.g.:"); println!(" 'tcp port 80'"); println!(" 'host 10.0.0.5'"); println!(" 'icmp and greater 1000'"); process::exit(1); } }; let lt = match Linktype::from_name(&layertype) { Ok(t) => t, Err(_) => { println!("Invalid linklayer type {}", layertype); process::exit(1); } }; let capture = Capture::dead(lt).unwrap(); let program: BpfProgram = match capture.compile(&prog, true) { Ok(p) => p, Err(e) => { println!("{:?}", e); process::exit(1); } }; let instructions = program.get_instructions(); let def: String = instructions .iter() .map(|ref op| format!("{}", op)) .collect::>() .join(","); println!("{},{}", instructions.len(), def); } pcap-2.2.0/examples/savefile.rs000064400000000000000000000021371046102023000145350ustar 00000000000000use pcap::*; fn main() { { // open capture from default device let device = Device::lookup() .expect("device lookup failed") .expect("no device available"); println!("Using device {}", device.name); // Setup Capture let mut cap = Capture::from_device(device) .unwrap() .immediate_mode(true) .open() .unwrap(); // open savefile using the capture let mut savefile = cap.savefile("test.pcap").unwrap(); // get a packet from the interface let p = cap.next_packet().unwrap(); // print the packet out println!("packet received on network: {:?}", p); // write the packet to the savefile savefile.write(&p); } // open a new capture from the test.pcap file we wrote to above let mut cap = Capture::from_file("test.pcap").unwrap(); // get a packet let p = cap.next_packet().unwrap(); // print that packet out -- it should be the same as the one we printed above println!("packet obtained from file: {:?}", p); } pcap-2.2.0/examples/savemultiplefiles.rs000064400000000000000000000014511046102023000164720ustar 00000000000000use pcap::Capture; fn main() { // get the default Device let device = pcap::Device::lookup().unwrap().unwrap(); // Setup Capture let mut cap = pcap::Capture::from_device(device) .unwrap() .immediate_mode(true) .open() .unwrap(); // remember linktype to create PCAP files later let linktype = cap.get_datalink(); // For example purposes we will only save 5 files... for counter in 0..5 { let mut save_file = Capture::dead(linktype) .unwrap() .savefile(format!("dump_{}.pcap", counter)) .unwrap(); // ...30 packets each for _ in 0..30 { let packet = cap.next_packet().unwrap(); save_file.write(&packet); } save_file.flush().unwrap(); } } pcap-2.2.0/examples/sendqueue.rs000064400000000000000000000033651046102023000147410ustar 00000000000000#[cfg(windows)] fn main() { const NUM_PACKETS: usize = 32; let args: Vec = std::env::args().collect(); if args.len() < 3 { println!("{} \n", args[0]); println!("Devices:"); let devs = pcap::Device::list().unwrap(); for dev in devs { println!("{}", dev.name); } return; } let mut cap = pcap::Capture::from_device(&*args[1]) .unwrap() .open() .unwrap(); let src_addr = eui48::MacAddress::parse_str("01:02:03:04:05:06").unwrap(); let dst_addr = eui48::MacAddress::parse_str(&args[2]).unwrap(); // 1MB send queue. let mut sq = pcap::sendqueue::SendQueue::new(1024 * 1024).unwrap(); let mut pktbuf = [0u8; 1514]; // typical L2 MTU // Prepare an L2 header for sending a raw ethernet packet from // 01:02:03:04:05 to the MAC address specified in argv[1]. The ethertype // will be set to 0x5555. pktbuf[0..6].copy_from_slice(dst_addr.as_bytes()); pktbuf[6..12].copy_from_slice(src_addr.as_bytes()); // big-endian encoding isn't important since we have a symmetrical value, // but we encode it for purpose of illustration. let ethertype: u16 = 0x5555; pktbuf[12..14].copy_from_slice(ðertype.to_be_bytes()); for idx in 0..NUM_PACKETS { let payload = &mut pktbuf[14..1514]; // Make the payload contain the packet index, u32 big-endian encoded. payload[0..4].copy_from_slice(&(idx as u32).to_be_bytes()); // Add 256 bytes of L2 payload sq.queue(None, &pktbuf[..14 + 256]).unwrap(); } sq.transmit(&mut cap, pcap::sendqueue::SendSync::Off) .unwrap(); } #[cfg(not(windows))] fn main() { eprintln!("Windows-only program"); } pcap-2.2.0/examples/stdin.rs000064400000000000000000000023311046102023000140540ustar 00000000000000//! Example of reading a pcap dump file stream from stdin. This is useful //! for integrating with other tools, such as tcpdump. For example, //! //! tcpdump -i en0 -U -w - | cargo run --example stdin //! #[cfg(not(windows))] mod inner { use pcap::{Packet, PacketCodec, PacketHeader}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct PacketOwned { pub header: PacketHeader, pub data: Box<[u8]>, } pub struct Codec; impl PacketCodec for Codec { type Item = PacketOwned; fn decode(&mut self, packet: Packet) -> Self::Item { PacketOwned { header: *packet.header, data: packet.data.into(), } } } } #[cfg(not(windows))] fn main() -> Result<(), Box> { use inner::*; use pcap::Capture; use std::{io, os::unix::io::AsRawFd}; let stdin = io::stdin(); let cap = unsafe { Capture::from_raw_fd(stdin.as_raw_fd())? }; for packet in cap.iter(Codec) { let packet = packet?; println!("{:?}", packet); } Ok(()) } #[cfg(windows)] fn main() -> Result<(), Box> { eprintln!("Program not supported on Windows"); Ok(()) } pcap-2.2.0/examples/streamecho.rs000064400000000000000000000023451046102023000150720ustar 00000000000000//! Example of using streams for an echo server. //! //! For brewity replies are sent with the same headers as the incoming //! packets. use futures::StreamExt; use pcap::{Active, Capture, Device, Error, Packet, PacketCodec, PacketStream}; use std::error; // Simple codec that returns owned copies, since the result may not // reference the input packet. pub struct BoxCodec; impl PacketCodec for BoxCodec { type Item = Box<[u8]>; fn decode(&mut self, packet: Packet) -> Self::Item { packet.data.into() } } fn new_stream(device: Device) -> Result, Error> { // get the default Device println!("Using device {}", device.name); let cap = Capture::from_device(device)? .immediate_mode(true) .open()? .setnonblock()?; cap.stream(BoxCodec) } #[tokio::main] async fn main() -> Result<(), Box> { let device = Device::lookup()?.ok_or("no device available")?; let mut stream = new_stream(device)?; loop { // Here in the event loop we may await a bunch of other // futures too, using the select! macro from tokio. let data = stream.next().await.unwrap()?; stream.capture_mut().sendpacket(data)?; } } pcap-2.2.0/examples/streamlisten.rs000064400000000000000000000026761046102023000154610ustar 00000000000000// This example explicitly creates and uses a single-threaded tokio // runtime. See streamlisten_mt.rs for an example using tokio macros // and multiple threads. // use futures::StreamExt; use pcap::{Active, Capture, Device, Error, Packet, PacketCodec, PacketStream}; pub struct SimpleDumpCodec; impl PacketCodec for SimpleDumpCodec { type Item = String; fn decode(&mut self, packet: Packet) -> Self::Item { format!("{:?}", packet) } } async fn start_new_stream(device: Device) -> PacketStream { match new_stream(device) { Ok(stream) => stream, Err(e) => { println!("{:?}", e); std::process::exit(1); } } } fn new_stream(device: Device) -> Result, Error> { // get the default Device println!("Using device {}", device.name); let cap = Capture::from_device(device)? .immediate_mode(true) .open()? .setnonblock()?; cap.stream(SimpleDumpCodec {}) } fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); let device = Device::lookup() .expect("device lookup failed") .expect("no device available"); let stream = rt.block_on(start_new_stream(device)); let fut = stream.for_each(move |s| { println!("{:?}", s); futures::future::ready(()) }); rt.block_on(fut); } pcap-2.2.0/examples/streamlisten_mt.rs000064400000000000000000000023501046102023000161460ustar 00000000000000use futures::StreamExt; use pcap::{Active, Capture, Device, Error, Packet, PacketCodec, PacketStream}; use std::error; pub struct SimpleDumpCodec; impl PacketCodec for SimpleDumpCodec { type Item = String; fn decode(&mut self, packet: Packet) -> Self::Item { format!("{:?}", packet) } } async fn start_new_stream(device: Device) -> PacketStream { match new_stream(device) { Ok(stream) => stream, Err(e) => { println!("{:?}", e); std::process::exit(1); } } } fn new_stream(device: Device) -> Result, Error> { // get the default Device println!("Using device {}", device.name); let cap = Capture::from_device(device)? .immediate_mode(true) .open()? .setnonblock()?; cap.stream(SimpleDumpCodec {}) } #[tokio::main(flavor = "multi_thread", worker_threads = 10)] async fn main() -> Result<(), Box> { let device = Device::lookup()?.ok_or("no device available")?; let stream = start_new_stream(device).await; let fut = stream.for_each(move |s| { println!("{:?}", s); futures::future::ready(()) }); fut.await; Ok(()) } pcap-2.2.0/msrv.lock000064400000000000000000001064761046102023000124270ustar 00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ "byteorder", "iovec", ] [[package]] name = "cc" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "crossbeam-deque" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" dependencies = [ "crossbeam-epoch", "crossbeam-utils", "maybe-uninit", ] [[package]] name = "crossbeam-epoch" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg", "cfg-if 0.1.10", "crossbeam-utils", "lazy_static", "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-queue" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ "cfg-if 0.1.10", "crossbeam-utils", "maybe-uninit", ] [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", "cfg-if 0.1.10", "lazy_static", ] [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "downcast" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "errno" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ "errno-dragonfly", "libc", "winapi 0.3.9", ] [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "etherparse" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "827292ea592108849932ad8e30218f8b1f21c0dfd0696698a18b5d0aed62d990" dependencies = [ "arrayvec", ] [[package]] name = "eui48" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "887418ac5e8d57c2e66e04bdc2fe15f9a5407be20b54a82c86bd0e368b709701" dependencies = [ "regex", "rustc-serialize", ] [[package]] name = "fastrand" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fragile" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ "bitflags 1.3.2", "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gat-std" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08eebf4c7afb1dbb5a68ece983498cf17a788cc076321d9fcc8a3f86da36493" dependencies = [ "gat-std-proc", ] [[package]] name = "gat-std-proc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c20aeb660d16a16f6611d5be552a02b68edd5c046574d03d2e791e7446a262f" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ "libc", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", "windows-targets 0.52.5", ] [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", "kernel32-sys", "libc", "log", "miow", "net2", "slab", "winapi 0.2.8", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "mio-uds" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", "mio 0.6.23", ] [[package]] name = "miow" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", "winapi 0.2.8", "ws2_32-sys", ] [[package]] name = "mockall" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if 1.0.0", "downcast", "fragile", "lazy_static", "mockall_derive", "predicates", "predicates-tree", ] [[package]] name = "mockall_derive" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "net2" version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ "lock_api", "parking_lot_core", "rustc_version", ] [[package]] name = "parking_lot_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" dependencies = [ "cfg-if 0.1.10", "cloudabi", "libc", "redox_syscall", "rustc_version", "smallvec", "winapi 0.3.9", ] [[package]] name = "pcap" version = "2.2.0" dependencies = [ "bitflags 1.3.2", "errno 0.2.8", "etherparse", "eui48", "futures 0.3.30", "gat-std", "libc", "libloading", "mockall", "once_cell", "pkg-config", "regex", "tempfile", "tokio 1.37.0", "tun-tap", "windows-sys 0.36.1", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "predicates" version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", "itertools", "normalize-line-endings", "predicates-core", "regex", ] [[package]] name = "predicates-core" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" [[package]] name = "predicates-tree" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-serialize" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ "semver", ] [[package]] name = "rustix" version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno 0.3.8", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "scoped-tls" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ "maybe-uninit", ] [[package]] name = "socket2" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "termtree" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "tokio" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes", "futures 0.1.31", "mio 0.6.23", "num_cpus", "tokio-codec", "tokio-current-thread", "tokio-executor", "tokio-fs", "tokio-io", "tokio-reactor", "tokio-sync", "tokio-tcp", "tokio-threadpool", "tokio-timer", "tokio-udp", "tokio-uds", ] [[package]] name = "tokio" version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "libc", "mio 0.8.11", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-codec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes", "futures 0.1.31", "tokio-io", ] [[package]] name = "tokio-core" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" dependencies = [ "bytes", "futures 0.1.31", "iovec", "log", "mio 0.6.23", "scoped-tls", "tokio 0.1.22", "tokio-executor", "tokio-io", "tokio-reactor", "tokio-timer", ] [[package]] name = "tokio-current-thread" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ "futures 0.1.31", "tokio-executor", ] [[package]] name = "tokio-executor" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils", "futures 0.1.31", ] [[package]] name = "tokio-fs" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ "futures 0.1.31", "tokio-io", "tokio-threadpool", ] [[package]] name = "tokio-io" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes", "futures 0.1.31", "log", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "tokio-reactor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils", "futures 0.1.31", "lazy_static", "log", "mio 0.6.23", "num_cpus", "parking_lot", "slab", "tokio-executor", "tokio-io", "tokio-sync", ] [[package]] name = "tokio-sync" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", "futures 0.1.31", ] [[package]] name = "tokio-tcp" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes", "futures 0.1.31", "iovec", "mio 0.6.23", "tokio-io", "tokio-reactor", ] [[package]] name = "tokio-threadpool" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils", "futures 0.1.31", "lazy_static", "log", "num_cpus", "slab", "tokio-executor", ] [[package]] name = "tokio-timer" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils", "futures 0.1.31", "slab", "tokio-executor", ] [[package]] name = "tokio-udp" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes", "futures 0.1.31", "log", "mio 0.6.23", "tokio-codec", "tokio-io", "tokio-reactor", ] [[package]] name = "tokio-uds" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes", "futures 0.1.31", "iovec", "libc", "log", "mio 0.6.23", "mio-uds", "tokio-codec", "tokio-io", "tokio-reactor", ] [[package]] name = "tun-tap" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a477a4e9367c72ac875d23cd07ac99ffa932497d8428767fed0cfa27bbabe50" dependencies = [ "cc", "futures 0.1.31", "libc", "mio 0.6.23", "tokio-core", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc 0.36.1", "windows_i686_gnu 0.36.1", "windows_i686_msvc 0.36.1", "windows_x86_64_gnu 0.36.1", "windows_x86_64_msvc 0.36.1", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ "winapi 0.2.8", "winapi-build", ] pcap-2.2.0/src/capture/activated/active.rs000064400000000000000000000117471046102023000166010ustar 00000000000000use std::borrow::Borrow; #[cfg(not(windows))] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use crate::{ capture::{Active, Capture}, raw, Error, }; impl Capture { /// Sends a packet over this capture handle's interface. pub fn sendpacket>(&mut self, buf: B) -> Result<(), Error> { let buf = buf.borrow(); self.check_err(unsafe { raw::pcap_sendpacket(self.handle.as_ptr(), buf.as_ptr() as _, buf.len() as _) == 0 }) } /// Set the capture to be non-blocking. When this is set, [`Self::next_packet()`] may return an /// error indicating that there is no packet available to be read. pub fn setnonblock(mut self) -> Result, Error> { Error::with_errbuf(|err| unsafe { if raw::pcap_setnonblock(self.handle.as_ptr(), 1, err) != 0 { return Err(Error::new(err)); } self.nonblock = true; Ok(self) }) } } #[cfg(not(windows))] impl AsRawFd for Capture { /// Returns the file descriptor for a live capture. fn as_raw_fd(&self) -> RawFd { let fd = unsafe { raw::pcap_fileno(self.handle.as_ptr()) }; assert!(fd != -1, "Unable to get file descriptor for live capture"); fd } } #[cfg(not(windows))] impl AsFd for Capture { /// Returns the file descriptor for a live capture. fn as_fd(&self) -> BorrowedFd { // SAFETY: pcap_fileno always succeeds on a live capture, // and we know this capture is live due to its State. let fd = unsafe { raw::pcap_fileno(self.handle.as_ptr()) }; assert!(fd != -1, "Unable to get file descriptor for live capture"); // SAFETY: The lifetime is bound to self, which is correct. // We have checked that fd != -1. unsafe { BorrowedFd::borrow_raw(fd) } } } #[cfg(test)] mod tests { use crate::{ capture::testmod::test_capture, raw::{ mock_ffi::*, testmod::{as_pcap_t, geterr_expect, RAWMTX}, }, }; use super::*; #[test] fn test_sendpacket() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let buffer: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let ctx = pcap_sendpacket_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once(|_, _, _| 0); let result = capture.sendpacket(buffer); assert!(result.is_ok()); let ctx = pcap_sendpacket_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once(|_, _, _| -1); let _err = geterr_expect(pcap); let result = capture.sendpacket(buffer); assert!(result.is_err()); } #[test] fn test_setnonblock() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; assert!(!capture.is_nonblock()); let ctx = pcap_setnonblock_context(); ctx.expect() .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1)) .return_once(|_, _, _| 0); let capture = capture.setnonblock().unwrap(); assert!(capture.is_nonblock()); } #[test] fn test_setnonblock_error() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; assert!(!capture.nonblock); let ctx = pcap_setnonblock_context(); ctx.expect() .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1)) .return_once(|_, _, _| -1); let result = capture.setnonblock(); assert!(result.is_err()); } #[test] #[cfg(not(windows))] fn test_as_raw_fd() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = pcap_fileno_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 7); assert_eq!(capture.as_raw_fd(), 7); } #[test] #[cfg(not(windows))] fn test_as_fd() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = pcap_fileno_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 7); assert_eq!(capture.as_fd().as_raw_fd(), 7); } } pcap-2.2.0/src/capture/activated/dead.rs000064400000000000000000000044621046102023000162170ustar 00000000000000use std::ptr::NonNull; use crate::{ capture::{Capture, Dead}, linktype::Linktype, raw, Error, }; #[cfg(libpcap_1_5_0)] use crate::capture::Precision; impl Capture { /// Creates a "fake" capture handle for the given link type. pub fn dead(linktype: Linktype) -> Result, Error> { let handle = unsafe { raw::pcap_open_dead(linktype.0, 65535) }; Ok(Capture::from( NonNull::::new(handle).ok_or(Error::InsufficientMemory)?, )) } /// Creates a "fake" capture handle for the given link type and timestamp precision. #[cfg(libpcap_1_5_0)] pub fn dead_with_precision( linktype: Linktype, precision: Precision, ) -> Result, Error> { let handle = unsafe { raw::pcap_open_dead_with_tstamp_precision(linktype.0, 65535, precision as u32) }; Ok(Capture::from( NonNull::::new(handle).ok_or(Error::InsufficientMemory)?, )) } } #[cfg(test)] mod tests { #[cfg(libpcap_1_5_0)] use mockall::predicate; use crate::raw::testmod::{as_pcap_t, RAWMTX}; use super::*; #[test] fn test_dead() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_open_dead_context(); ctx.expect().return_once_st(move |_, _| pcap); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let result = Capture::dead(Linktype::ETHERNET); assert!(result.is_ok()); } #[test] #[cfg(libpcap_1_5_0)] fn test_dead_with_precision() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_open_dead_with_tstamp_precision_context(); ctx.expect() .with(predicate::always(), predicate::always(), predicate::eq(1)) .return_once_st(move |_, _, _| pcap); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let result = Capture::dead_with_precision(Linktype::ETHERNET, Precision::Nano); assert!(result.is_ok()); } } pcap-2.2.0/src/capture/activated/iterator.rs000064400000000000000000000171251046102023000171530ustar 00000000000000use crate::{ capture::{Activated, Capture}, codec::PacketCodec, Error, }; /// Implement an Iterator of Packet pub struct PacketIter { capture: Capture, codec: C, } impl PacketIter { pub(crate) fn new(capture: Capture, codec: C) -> Self { Self { capture, codec } } /// Returns a mutable reference to the inner [`Capture`]. pub fn capture_mut(&mut self) -> &mut Capture { &mut self.capture } } impl From> for (Capture, C) { fn from(iter: PacketIter) -> Self { (iter.capture, iter.codec) } } impl Iterator for PacketIter { type Item = Result; fn next(&mut self) -> Option { match self.capture.next_packet() { Ok(packet) => Some(Ok(self.codec.decode(packet))), Err(Error::NoMorePackets) => None, Err(e) => Some(Err(e)), } } } #[cfg(feature = "lending-iter")] mod lending_iter { use crate::Activated; use crate::Capture; use crate::Error; use crate::Packet; use gat_std::iter::{IntoIterator, Iterator}; pub struct PacketLendingIter { capture: Capture, } impl IntoIterator for Capture { type IntoIter = PacketLendingIter; fn into_iter(self) -> Self::IntoIter { PacketLendingIter { capture: self } } } impl Iterator for PacketLendingIter { type Item<'a> = Result, Error>; fn next(&mut self) -> Option> { match self.capture.next_packet() { Ok(packet) => Some(Ok(packet)), Err(Error::NoMorePackets) => None, Err(e) => Some(Err(e)), } } } } #[cfg(test)] mod tests { use crate::{ capture::{ activated::testmod::{next_ex_expect, PACKET}, testmod::test_capture, Active, Offline, }, codec::testmod::Codec, raw::{ self, testmod::{as_pcap_t, geterr_expect, RAWMTX}, }, }; #[cfg(feature = "lending-iter")] use gat_std::iter::{IntoIterator, Iterator}; use super::*; #[cfg(feature = "lending-iter")] use super::lending_iter::*; #[test] fn test_iter_next() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter = capture.iter(Codec); let _nxt = next_ex_expect(pcap); let next = packet_iter.next().unwrap(); let next_packet = next.unwrap(); assert_eq!(next_packet.header, *PACKET.header); assert_eq!(*next_packet.data, *PACKET.data); let _nxt = next_ex_expect(pcap); let next_packet = packet_iter.capture_mut().next_packet().unwrap(); assert_eq!(next_packet, PACKET); let _nxt = next_ex_expect(pcap); let (mut capture, _) = packet_iter.into(); let next_packet = capture.next_packet().unwrap(); assert_eq!(next_packet, PACKET); } #[test] fn test_iter_timeout() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter = capture.iter(Codec); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| 0); let next = packet_iter.next().unwrap(); let err = next.unwrap_err(); assert_eq!(err, Error::TimeoutExpired); } #[test] fn test_next_packet_read_error() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter = capture.iter(Codec); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -1); let _err = geterr_expect(pcap); let next = packet_iter.next().unwrap(); assert!(next.is_err()); } #[test] fn test_next_packet_no_more_packets() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter = capture.iter(Codec); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -2); let next = packet_iter.next(); assert!(next.is_none()); } #[test] #[cfg(feature = "lending-iter")] fn test_lending_iter() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter: PacketLendingIter = capture.into_iter(); let _nxt = next_ex_expect(pcap); let next = packet_iter.next().unwrap(); let next_packet = next.unwrap(); assert_eq!(next_packet, PACKET); } #[test] #[cfg(feature = "lending-iter")] fn test_lending_iter_timeout() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter: PacketLendingIter = capture.into_iter(); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| 0); let next = packet_iter.next().unwrap(); let err = next.unwrap_err(); assert_eq!(err, Error::TimeoutExpired); } #[test] #[cfg(feature = "lending-iter")] fn test_lending_iter_read_error() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter: PacketLendingIter = capture.into_iter(); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -1); let _err = geterr_expect(pcap); let next = packet_iter.next().unwrap(); assert!(next.is_err()); } #[test] #[cfg(feature = "lending-iter")] fn test_lending_iter_no_more_packets() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let mut packet_iter: PacketLendingIter = capture.into_iter(); let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -2); let next = packet_iter.next(); assert!(next.is_none()); } } pcap-2.2.0/src/capture/activated/mod.rs000064400000000000000000001041311046102023000160730ustar 00000000000000pub mod active; pub mod dead; pub mod iterator; pub mod offline; use std::{ any::Any, convert::TryInto, ffi::CString, fmt, mem, panic::{catch_unwind, resume_unwind, AssertUnwindSafe}, path::Path, ptr::{self, NonNull}, slice, }; #[cfg(not(windows))] use std::os::unix::io::RawFd; use crate::{ capture::{Activated, Capture}, codec::PacketCodec, linktype::Linktype, packet::{Packet, PacketHeader}, raw, Error, }; use iterator::PacketIter; #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// Packet statistics for a capture pub struct Stat { /// Number of packets received pub received: u32, /// Number of packets dropped because there was no room in the operating system's buffer when /// they arrived, because packets weren't being read fast enough pub dropped: u32, /// Number of packets dropped by the network interface or its driver pub if_dropped: u32, } impl Stat { fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat { Stat { received, dropped, if_dropped, } } } #[repr(u32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] /// The direction of packets to be captured. Use with `Capture::direction`. pub enum Direction { /// Capture packets received by or sent by the device. This is the default. InOut = raw::PCAP_D_INOUT, /// Only capture packets received by the device. In = raw::PCAP_D_IN, /// Only capture packets sent by the device. Out = raw::PCAP_D_OUT, } ///# Activated captures include `Capture` and `Capture`. impl Capture { /// List the datalink types that this captured device supports. pub fn list_datalinks(&self) -> Result, Error> { unsafe { let mut links: *mut i32 = ptr::null_mut(); let num = raw::pcap_list_datalinks(self.handle.as_ptr(), &mut links); let mut vec = vec![]; if num > 0 { vec.extend( slice::from_raw_parts(links, num as _) .iter() .cloned() .map(Linktype), ) } raw::pcap_free_datalinks(links); self.check_err(num > 0).and(Ok(vec)) } } /// Set the datalink type for the current capture handle. pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> { self.check_err(unsafe { raw::pcap_set_datalink(self.handle.as_ptr(), linktype.0) == 0 }) } /// Get the current datalink type for this capture handle. pub fn get_datalink(&self) -> Linktype { unsafe { Linktype(raw::pcap_datalink(self.handle.as_ptr())) } } /// Create a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. pub fn savefile>(&self, path: P) -> Result { let name = CString::new(path.as_ref().to_str().unwrap())?; let handle_opt = NonNull::::new(unsafe { raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr()) }); let handle = self .check_err(handle_opt.is_some()) .map(|_| handle_opt.unwrap())?; Ok(Savefile::from(handle)) } /// Create a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. The output is written to a raw file descriptor which is opened in `"w"` /// mode. /// /// # Safety /// /// Unsafe, because the returned Savefile assumes it is the sole owner of the file descriptor. #[cfg(not(windows))] pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result { open_raw_fd(fd, b'w').and_then(|file| { let handle_opt = NonNull::::new(raw::pcap_dump_fopen( self.handle.as_ptr(), file, )); let handle = self .check_err(handle_opt.is_some()) .map(|_| handle_opt.unwrap())?; Ok(Savefile::from(handle)) }) } /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. This is similar to `savefile()` but does not create the file if it /// does not exist and, if it does already exist, and is a pcap file with the same /// byte order as the host opening the file, and has the same time stamp precision, /// link-layer header type, and snapshot length as p, it will write new packets /// at the end of the file. #[cfg(libpcap_1_7_2)] pub fn savefile_append>(&self, path: P) -> Result { let name = CString::new(path.as_ref().to_str().unwrap())?; let handle_opt = NonNull::::new(unsafe { raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr()) }); let handle = self .check_err(handle_opt.is_some()) .map(|_| handle_opt.unwrap())?; Ok(Savefile::from(handle)) } /// Set the direction of the capture pub fn direction(&self, direction: Direction) -> Result<(), Error> { self.check_err(unsafe { raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0 }) } /// Blocks until a packet is returned from the capture handle or an error occurs. /// /// pcap captures packets and places them into a buffer which this function reads /// from. /// /// # Warning /// /// This buffer has a finite length, so if the buffer fills completely new /// packets will be discarded temporarily. This means that in realtime situations, /// you probably want to minimize the time between calls to next_packet() method. pub fn next_packet(&mut self) -> Result, Error> { unsafe { let mut header: *mut raw::pcap_pkthdr = ptr::null_mut(); let mut packet: *const libc::c_uchar = ptr::null(); let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet); match retcode { i if i >= 1 => { // packet was read without issue Ok(Packet::new( &*(&*header as *const raw::pcap_pkthdr as *const PacketHeader), slice::from_raw_parts(packet, (*header).caplen as _), )) } 0 => { // packets are being read from a live capture and the // timeout expired Err(Error::TimeoutExpired) } -1 => { // an error occured while reading the packet Err(self.get_err()) } -2 => { // packets are being read from a "savefile" and there are no // more packets to read Err(Error::NoMorePackets) } // GRCOV_EXCL_START _ => { // libpcap only defines codes >=1, 0, -1, and -2 unreachable!() } // GRCOV_EXCL_STOP } } } /// Return an iterator that call [`Self::next_packet()`] forever. Require a [`PacketCodec`] pub fn iter(self, codec: C) -> PacketIter { PacketIter::new(self, codec) } pub fn for_each(&mut self, count: Option, handler: F) -> Result<(), Error> where F: FnMut(Packet), { let cnt = match count { // Actually passing 0 down to pcap_loop would mean read forever. // We interpret it as "read nothing", so we just succeed immediately. Some(0) => return Ok(()), Some(cnt) => cnt .try_into() .expect("count of packets to read cannot exceed c_int::MAX"), None => -1, }; let mut handler = Handler { func: AssertUnwindSafe(handler), panic_payload: None, handle: self.handle, }; let return_code = unsafe { raw::pcap_loop( self.handle.as_ptr(), cnt, Handler::::callback, &mut handler as *mut Handler> as *mut u8, ) }; if let Some(e) = handler.panic_payload { resume_unwind(e); } self.check_err(return_code == 0) } /// Compiles the string into a filter program using `pcap_compile`. pub fn compile(&self, program: &str, optimize: bool) -> Result { let program = CString::new(program)?; unsafe { let mut bpf_program: raw::bpf_program = mem::zeroed(); let ret = raw::pcap_compile( self.handle.as_ptr(), &mut bpf_program, program.as_ptr(), optimize as libc::c_int, 0, ); self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program))) } } /// Sets the filter on the capture using the given BPF program string. Internally this is /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting /// code is performed /// /// See for more information about this syntax. pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> { let mut bpf_program = self.compile(program, optimize)?; let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) }; self.check_err(ret != -1) } /// Get capture statistics about this capture. The values represent packet statistics from the /// start of the run to the time of the call. /// /// See for per-platform caveats about /// how packet statistics are calculated. pub fn stats(&mut self) -> Result { unsafe { let mut stats: raw::pcap_stat = mem::zeroed(); self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1) .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop)) } } } // Handler and its associated function let us create an extern "C" fn which dispatches to a normal // Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this // generic parameter is to ensure that in Capture::pcap_loop that we pass the right function // pointer and the right data pointer to pcap_loop. struct Handler { func: F, panic_payload: Option>, handle: NonNull, } impl Handler where F: FnMut(Packet), { extern "C" fn callback( slf: *mut libc::c_uchar, header: *const raw::pcap_pkthdr, packet: *const libc::c_uchar, ) { unsafe { let packet = Packet::new( &*(header as *const PacketHeader), slice::from_raw_parts(packet, (*header).caplen as _), ); let slf = slf as *mut Self; let func = &mut (*slf).func; let mut func = AssertUnwindSafe(func); // If our handler function panics, we need to prevent it from unwinding across the // FFI boundary. If the handler panics we catch the unwind here, break out of // pcap_loop, and resume the unwind outside. if let Err(e) = catch_unwind(move || func(packet)) { (*slf).panic_payload = Some(e); raw::pcap_breakloop((*slf).handle.as_ptr()); } } } } impl From> for Capture { fn from(cap: Capture) -> Capture { unsafe { mem::transmute(cap) } } } /// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`. pub struct Savefile { handle: NonNull, } // Just like a Capture, a Savefile is safe to Send as it encapsulates the entire lifetime of // `raw::pcap_dumper_t *`, but it is not safe to Sync as libpcap does not promise thread-safe access // to the same `raw::pcap_dumper_t *` from multiple threads. unsafe impl Send for Savefile {} impl Savefile { /// Write a packet to a capture file pub fn write(&mut self, packet: &Packet<'_>) { unsafe { raw::pcap_dump( self.handle.as_ptr() as _, &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr), packet.data.as_ptr(), ); } } /// Flushes all the packets that haven't been written to the savefile pub fn flush(&mut self) -> Result<(), Error> { if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 { return Err(Error::ErrnoError(errno::errno())); } Ok(()) } } impl From> for Savefile { fn from(handle: NonNull) -> Self { Savefile { handle } } } impl Drop for Savefile { fn drop(&mut self) { unsafe { raw::pcap_dump_close(self.handle.as_ptr()) } } } #[repr(transparent)] pub struct BpfInstruction(raw::bpf_insn); #[repr(transparent)] pub struct BpfProgram(raw::bpf_program); impl BpfProgram { /// checks whether a filter matches a packet pub fn filter(&self, buf: &[u8]) -> bool { let header: raw::pcap_pkthdr = raw::pcap_pkthdr { ts: libc::timeval { tv_sec: 0, tv_usec: 0, }, caplen: buf.len() as u32, len: buf.len() as u32, }; unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 } } pub fn get_instructions(&self) -> &[BpfInstruction] { unsafe { slice::from_raw_parts( self.0.bf_insns as *const BpfInstruction, self.0.bf_len as usize, ) } } } impl Drop for BpfProgram { fn drop(&mut self) { unsafe { raw::pcap_freecode(&mut self.0) } } } impl fmt::Display for BpfInstruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} {} {} {}", self.0.code, self.0.jt, self.0.jf, self.0.k ) } } unsafe impl Send for BpfProgram {} #[cfg(not(windows))] /// Open a raw file descriptor. /// /// # Safety /// /// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor. pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> { let mode = [mode, 0]; libc::fdopen(fd, mode.as_ptr() as _) .as_mut() .map(|f| f as _) .ok_or(Error::InvalidRawFd) } // GRCOV_EXCL_START #[cfg(test)] mod testmod { use super::*; pub static TS: libc::timeval = libc::timeval { tv_sec: 5, tv_usec: 50, }; pub static LEN: u32 = DATA.len() as u32; pub static CAPLEN: u32 = LEN; pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr { ts: TS, caplen: CAPLEN, len: LEN, }; pub static PACKET_HEADER: PacketHeader = PacketHeader { ts: TS, caplen: CAPLEN, len: LEN, }; pub static DATA: [u8; 4] = [4, 5, 6, 7]; pub static PACKET: Packet = Packet { header: &PACKET_HEADER, data: &DATA, }; pub struct NextExContext(raw::__pcap_next_ex::Context); pub fn next_ex_expect(pcap: *mut raw::pcap_t) -> NextExContext { let data_ptr: *const libc::c_uchar = DATA.as_ptr(); let pkthdr_ptr: *mut raw::pcap_pkthdr = unsafe { std::ptr::addr_of_mut!(PKTHDR) }; let ctx = raw::pcap_next_ex_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, arg2, arg3| { unsafe { *arg2 = pkthdr_ptr; *arg3 = data_ptr; } CAPLEN as i32 }); NextExContext(ctx) } } // GRCOV_EXCL_STOP #[cfg(test)] mod tests { use crate::{ capture::{ activated::testmod::{next_ex_expect, PACKET}, testmod::test_capture, Active, Capture, Offline, }, raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX}, }; use super::*; #[test] fn test_list_datalinks() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_list_datalinks_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(|_, _| 0); let ctx = raw::pcap_free_datalinks_context(); ctx.expect().return_once(|_| {}); let _err = geterr_expect(pcap); let result = capture.list_datalinks(); assert!(result.is_err()); let mut datalinks: [i32; 4] = [0, 1, 2, 3]; let links: *mut i32 = datalinks.as_mut_ptr(); let len = datalinks.len(); let ctx = raw::pcap_list_datalinks_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(move |_, arg2| { unsafe { *arg2 = links }; len as i32 }); let ctx = raw::pcap_free_datalinks_context(); ctx.checkpoint(); ctx.expect().return_once(|_| {}); let pcap_datalinks = capture.list_datalinks().unwrap(); assert_eq!( pcap_datalinks, datalinks.iter().cloned().map(Linktype).collect::>() ); } #[test] fn test_set_datalink() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_set_datalink_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let result = capture.set_datalink(Linktype::ETHERNET); assert!(result.is_ok()); let ctx = raw::pcap_set_datalink_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| -1); let _err = geterr_expect(pcap); let result = capture.set_datalink(Linktype::ETHERNET); assert!(result.is_err()); } #[test] fn test_get_datalink() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_datalink_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 1); let linktype = capture.get_datalink(); assert_eq!(linktype, Linktype::ETHERNET); } #[test] fn unify_activated() { #![allow(dead_code)] fn test1() -> Capture { panic!(); } fn test2() -> Capture { panic!(); } fn maybe(a: bool) -> Capture { if a { test1().into() } else { test2().into() } } fn also_maybe(a: &mut Capture) { a.filter("whatever filter string, this won't be run anyway", false) .unwrap(); } } #[test] fn test_savefile() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let mut value: isize = 888; let pcap_dumper = as_pcap_dumper_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_dump_open_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(move |_, _| pcap_dumper); let ctx = raw::pcap_dump_close_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap_dumper) .return_once(|_| {}); let result = capture.savefile("path/to/nowhere"); assert!(result.is_ok()); } #[test] #[cfg(libpcap_1_7_2)] fn test_savefile_append() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let mut value: isize = 888; let pcap_dumper = as_pcap_dumper_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_dump_open_append_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(move |_, _| pcap_dumper); let ctx = raw::pcap_dump_close_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap_dumper) .return_once(|_| {}); let result = capture.savefile_append("path/to/nowhere"); assert!(result.is_ok()); } #[test] fn test_savefile_error() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_dump_open_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| std::ptr::null_mut()); let _err = geterr_expect(pcap); let result = capture.savefile("path/to/nowhere"); assert!(result.is_err()); } #[test] #[cfg(libpcap_1_7_2)] fn test_savefile_append_error() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_dump_open_append_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| std::ptr::null_mut()); let _err = geterr_expect(pcap); let result = capture.savefile_append("path/to/nowhere"); assert!(result.is_err()); } #[test] fn test_savefile_ops() { let _m = RAWMTX.lock(); let mut value: isize = 888; let pcap_dumper = as_pcap_dumper_t(&mut value); let ctx = raw::pcap_dump_close_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap_dumper) .return_once(|_| {}); let mut savefile = Savefile { handle: NonNull::new(pcap_dumper).unwrap(), }; let ctx = raw::pcap_dump_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _) .return_once(|_, _, _| {}); savefile.write(&PACKET); let ctx = raw::pcap_dump_flush_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap_dumper) .return_once(|_| 0); let result = savefile.flush(); assert!(result.is_ok()); let ctx = raw::pcap_dump_flush_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap_dumper) .return_once(|_| -1); let result = savefile.flush(); assert!(result.is_err()); } #[test] fn test_direction() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_setdirection_context(); ctx.expect() .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT)) .return_once(|_, _| 0); let result = capture.direction(Direction::Out); assert!(result.is_ok()); let ctx = raw::pcap_setdirection_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT)) .return_once(|_, _| -1); let _err = geterr_expect(pcap); let result = capture.direction(Direction::Out); assert!(result.is_err()); // For code coverage of the derive line. assert_ne!(Direction::In, Direction::InOut); assert_ne!(Direction::In, Direction::Out); assert_ne!(Direction::InOut, Direction::Out); } #[test] fn test_next_packet() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let _nxt = next_ex_expect(pcap); let next_packet = capture.next_packet().unwrap(); assert_eq!(next_packet, PACKET); } #[test] fn test_next_packet_timeout() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| 0); let err = capture.next_packet().unwrap_err(); assert_eq!(err, Error::TimeoutExpired); } #[test] fn test_next_packet_read_error() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -1); let _err = geterr_expect(pcap); let result = capture.next_packet(); assert!(result.is_err()); } #[test] fn test_next_packet_no_more_packets() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let ctx = raw::pcap_next_ex_context(); ctx.expect() .withf_st(move |arg1, _, _| *arg1 == pcap) .return_once_st(move |_, _, _| -2); let err = capture.next_packet().unwrap_err(); assert_eq!(err, Error::NoMorePackets); } #[test] fn test_compile() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_compile_context(); ctx.expect() .withf_st(move |arg1, _, _, _, _| *arg1 == pcap) .return_once(|_, _, _, _, _| -1); let _err = geterr_expect(pcap); let ctx = raw::pcap_freecode_context(); ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); assert!(result.is_err()); let ctx = raw::pcap_compile_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _, _, _, _| *arg1 == pcap) .return_once(|_, _, _, _, _| 0); let ctx = raw::pcap_freecode_context(); ctx.checkpoint(); ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); assert!(result.is_ok()); } #[test] fn test_filter() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let ctx = raw::pcap_compile_context(); ctx.expect() .withf_st(move |arg1, _, _, _, _| *arg1 == pcap) .return_once(|_, _, _, _, _| 0); let ctx = raw::pcap_setfilter_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| -1); let _err = geterr_expect(pcap); let ctx = raw::pcap_freecode_context(); ctx.expect().return_once(|_| {}); let result = capture.filter("some bpf program", false); assert!(result.is_err()); let ctx = raw::pcap_compile_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _, _, _, _| *arg1 == pcap) .return_once(|_, _, _, _, _| 0); let ctx = raw::pcap_setfilter_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let ctx = raw::pcap_freecode_context(); ctx.checkpoint(); ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); assert!(result.is_ok()); } #[test] fn test_stats() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture = test_capture.capture; let stat = raw::pcap_stat { ps_recv: 1, ps_drop: 2, ps_ifdrop: 3, }; let ctx = raw::pcap_stats_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(move |_, arg2| { unsafe { *arg2 = stat }; 0 }); let stats = capture.stats().unwrap(); assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop)); let ctx = raw::pcap_stats_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once_st(move |_, _| -1); let _err = geterr_expect(pcap); let result = capture.stats(); assert!(result.is_err()); } #[test] fn test_bpf_instruction_display() { let instr = BpfInstruction(raw::bpf_insn { code: 1, jt: 2, jf: 3, k: 4, }); assert_eq!(format!("{}", instr), "1 2 3 4"); } #[test] fn read_packet_via_pcap_loop() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_loop_context(); ctx.expect() .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1) .return_once_st(move |_, _, func, data| { let header = raw::pcap_pkthdr { ts: libc::timeval { tv_sec: 0, tv_usec: 0, }, caplen: 0, len: 0, }; let packet_data = &[]; func(data, &header, packet_data.as_ptr()); 0 }); let mut packets = 0; capture .for_each(None, |_| { packets += 1; }) .unwrap(); assert_eq!(packets, 1); } #[test] #[should_panic = "panic in callback"] fn panic_in_pcap_loop() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_loop_context(); ctx.expect() .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1) .return_once_st(move |_, _, func, data| { let header = raw::pcap_pkthdr { ts: libc::timeval { tv_sec: 0, tv_usec: 0, }, caplen: 0, len: 0, }; let packet_data = &[]; func(data, &header, packet_data.as_ptr()); 0 }); let ctx = raw::pcap_breakloop_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once_st(move |_| {}); capture .for_each(None, |_| panic!("panic in callback")) .unwrap(); } #[test] fn for_each_with_count() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture: Capture = test_capture.capture.into(); let ctx = raw::pcap_loop_context(); ctx.expect() .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2) .return_once_st(move |_, _, func, data| { let header = raw::pcap_pkthdr { ts: libc::timeval { tv_sec: 0, tv_usec: 0, }, caplen: 0, len: 0, }; let packet_data = &[]; func(data, &header, packet_data.as_ptr()); func(data, &header, packet_data.as_ptr()); 0 }); let mut packets = 0; capture .for_each(Some(2), |_| { packets += 1; }) .unwrap(); assert_eq!(packets, 2); } #[test] fn for_each_with_count_0() { let _m = RAWMTX.lock(); let mut value: isize = 777; let pcap = as_pcap_t(&mut value); let test_capture = test_capture::(pcap); let mut capture: Capture = test_capture.capture.into(); let mut packets = 0; capture .for_each(Some(0), |_| { packets += 1; }) .unwrap(); assert_eq!(packets, 0); } } pcap-2.2.0/src/capture/activated/offline.rs000064400000000000000000000113231046102023000167360ustar 00000000000000use std::path::Path; #[cfg(not(windows))] use std::os::unix::io::RawFd; use crate::{ capture::{Capture, Offline}, raw, Error, }; #[cfg(libpcap_1_5_0)] use crate::capture::Precision; #[cfg(not(windows))] use crate::capture::activated::open_raw_fd; impl Capture { /// Opens an offline capture handle from a pcap dump file, given a path. pub fn from_file>(path: P) -> Result, Error> { Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe { raw::pcap_open_offline(path, err) }) } /// Opens an offline capture handle from a pcap dump file, given a path. /// Takes an additional precision argument specifying the time stamp precision desired. #[cfg(libpcap_1_5_0)] pub fn from_file_with_precision>( path: P, precision: Precision, ) -> Result, Error> { Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe { raw::pcap_open_offline_with_tstamp_precision(path, precision as _, err) }) } /// Opens an offline capture handle from a pcap dump file, given a file descriptor. /// /// # Safety /// /// Unsafe, because the returned Capture assumes it is the sole owner of the file descriptor. #[cfg(not(windows))] pub unsafe fn from_raw_fd(fd: RawFd) -> Result, Error> { open_raw_fd(fd, b'r') .and_then(|file| Capture::new_raw(None, |_, err| raw::pcap_fopen_offline(file, err))) } /// Opens an offline capture handle from a pcap dump file, given a file descriptor. Takes an /// additional precision argument specifying the time stamp precision desired. /// /// # Safety /// /// Unsafe, because the returned Capture assumes it is the sole owner of the file descriptor. #[cfg(all(not(windows), libpcap_1_5_0))] pub unsafe fn from_raw_fd_with_precision( fd: RawFd, precision: Precision, ) -> Result, Error> { open_raw_fd(fd, b'r').and_then(|file| { Capture::new_raw(None, |_, err| { raw::pcap_fopen_offline_with_tstamp_precision(file, precision as _, err) }) }) } /// Get the major version number of the pcap dump file format. pub fn major_version(&self) -> i32 { unsafe { raw::pcap_major_version(self.handle.as_ptr()) } } /// Get the minor version number of the pcap dump file format. pub fn minor_version(&self) -> i32 { unsafe { raw::pcap_minor_version(self.handle.as_ptr()) } } /// Get the (major, minor) version number of the pcap dump file format. pub fn version(&self) -> (i32, i32) { (self.major_version(), self.minor_version()) } } #[cfg(test)] mod tests { #[cfg(libpcap_1_5_0)] use mockall::predicate; use crate::{ capture::testmod::test_capture, raw::testmod::{as_pcap_t, RAWMTX}, }; use super::*; #[test] fn test_from_file() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_open_offline_context(); ctx.expect().return_once_st(move |_, _| pcap); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let result = Capture::from_file("path/to/nowhere"); assert!(result.is_ok()); } #[test] #[cfg(libpcap_1_5_0)] fn test_from_file_with_precision() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_open_offline_with_tstamp_precision_context(); ctx.expect() .with(predicate::always(), predicate::eq(1), predicate::always()) .return_once_st(move |_, _, _| pcap); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let result = Capture::from_file_with_precision("path/to/nowhere", Precision::Nano); assert!(result.is_ok()); } #[test] fn test_version() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_major_version_context(); ctx.expect() .withf_st(move |arg| *arg == pcap) .return_once(|_| 5); let ctx = raw::pcap_minor_version_context(); ctx.expect() .withf_st(move |arg| *arg == pcap) .return_once(|_| 7); let test_capture = test_capture::(pcap); let capture = test_capture.capture; assert_eq!(capture.version(), (5, 7)); } } pcap-2.2.0/src/capture/inactive.rs000064400000000000000000000347171046102023000151660ustar 00000000000000use std::mem; use crate::{ capture::{Active, Capture, Inactive}, device::Device, raw, Error, }; #[cfg(libpcap_1_5_0)] use crate::capture::Precision; impl Capture { /// Opens a capture handle for a device. You can pass a `Device` or an `&str` device /// name here. The handle is inactive, but can be activated via `.open()`. /// /// # Example /// ``` /// use pcap::*; /// /// // Usage 1: Capture from a single owned device /// let dev: Device = pcap::Device::lookup() /// .expect("device lookup failed") /// .expect("no device available"); /// let cap1 = Capture::from_device(dev); /// /// // Usage 2: Capture from an element of device list. /// let list: Vec = pcap::Device::list().unwrap(); /// let cap2 = Capture::from_device(list[0].clone()); /// /// // Usage 3: Capture from `&str` device name /// let cap3 = Capture::from_device("eth0"); /// ``` pub fn from_device>(device: D) -> Result, Error> { let device: Device = device.into(); Capture::new_raw(Some(&device.name), |name, err| unsafe { raw::pcap_create(name, err) }) } /// Activates an inactive capture created from `Capture::from_device()` or returns an error. pub fn open(self) -> Result, Error> { unsafe { self.check_err(raw::pcap_activate(self.handle.as_ptr()) == 0)?; Ok(mem::transmute::, Capture>(self)) } } /// Set the read timeout for the Capture. By default, this is 0, so it will block indefinitely. pub fn timeout(self, ms: i32) -> Capture { unsafe { raw::pcap_set_timeout(self.handle.as_ptr(), ms) }; self } /// Set the time stamp type to be used by a capture device. #[cfg(libpcap_1_2_1)] pub fn tstamp_type(self, tstamp_type: TimestampType) -> Capture { unsafe { raw::pcap_set_tstamp_type(self.handle.as_ptr(), tstamp_type as _) }; self } /// Set promiscuous mode on or off. By default, this is off. pub fn promisc(self, to: bool) -> Capture { unsafe { raw::pcap_set_promisc(self.handle.as_ptr(), to as _) }; self } /// Set immediate mode on or off. By default, this is off. /// /// Note that in WinPcap immediate mode is set by passing a 0 argument to `min_to_copy`. /// Immediate mode will be unset if `min_to_copy` is later called with a non-zero argument. /// Immediate mode is unset by resetting `min_to_copy` to the WinPcap default possibly changing /// a previously set value. When using `min_to_copy`, it is best to avoid `immediate_mode`. #[cfg(any(libpcap_1_5_0, windows))] pub fn immediate_mode(self, to: bool) -> Capture { // Prior to 1.5.0 when `pcap_set_immediate_mode` was introduced, the necessary steps to set // immediate mode were more complicated, depended on the OS, and in some configurations had // to be set on an active capture. See // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html. Since we do not // expect pre-1.5.0 version on unix systems in the wild, we simply ignore those cases. #[cfg(libpcap_1_5_0)] unsafe { raw::pcap_set_immediate_mode(self.handle.as_ptr(), to as _) }; // In WinPcap we use `pcap_setmintocopy` as it does not have `pcap_set_immediate_mode`. #[cfg(all(windows, not(libpcap_1_5_0)))] unsafe { raw::pcap_setmintocopy( self.handle.as_ptr(), if to { 0 } else { raw::WINPCAP_MINTOCOPY_DEFAULT }, ) }; self } /// Set want_pktap to true or false. The default is maintained by libpcap. #[cfg(all(libpcap_1_5_3, target_os = "macos"))] pub fn want_pktap(self, to: bool) -> Capture { unsafe { raw::pcap_set_want_pktap(self.handle.as_ptr(), to as _) }; self } /// Set rfmon mode on or off. The default is maintained by pcap. #[cfg(not(windows))] pub fn rfmon(self, to: bool) -> Capture { unsafe { raw::pcap_set_rfmon(self.handle.as_ptr(), to as _) }; self } /// Set the buffer size for incoming packet data. /// /// The default is 1000000. This should always be larger than the snaplen. pub fn buffer_size(self, to: i32) -> Capture { unsafe { raw::pcap_set_buffer_size(self.handle.as_ptr(), to) }; self } /// Set the time stamp precision returned in captures. #[cfg(libpcap_1_5_0)] pub fn precision(self, precision: Precision) -> Capture { unsafe { raw::pcap_set_tstamp_precision(self.handle.as_ptr(), precision as _) }; self } /// Set the snaplen size (the maximum length of a packet captured into the buffer). /// Useful if you only want certain headers, but not the entire packet. /// /// The default is 65535. pub fn snaplen(self, to: i32) -> Capture { unsafe { raw::pcap_set_snaplen(self.handle.as_ptr(), to) }; self } } #[repr(i32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] /// Timestamp types /// /// Not all systems and interfaces will necessarily support all of these. /// /// Note that time stamps synchronized with the system clock can go backwards, as the system clock /// can go backwards. If a clock is not in sync with the system clock, that could be because the /// system clock isn't keeping accurate time, because the other clock isn't keeping accurate time, /// or both. /// /// Note that host-provided time stamps generally correspond to the time when the time-stamping /// code sees the packet; this could be some unknown amount of time after the first or last bit of /// the packet is received by the network adapter, due to batching of interrupts for packet /// arrival, queueing delays, etc.. pub enum TimestampType { /// Timestamps are provided by the host machine, rather than by the capture device. /// /// The characteristics of the timestamp are unknown. Host = 0, /// A timestamp provided by the host machine that is low precision but relatively cheap to /// fetch. /// /// This is normally done using the system clock, so it's normally synchornized with times /// you'd fetch from system calls. HostLowPrec = 1, /// A timestamp provided by the host machine that is high precision. It might be more expensive /// to fetch. /// /// The timestamp might or might not be synchronized with the system clock, and might have /// problems with time stamps for packets received on different CPUs, depending on the /// platform. HostHighPrec = 2, /// The timestamp is a high-precision time stamp supplied by the capture device. /// /// The timestamp is synchronized with the system clock. Adapter = 3, /// The timestamp is a high-precision time stamp supplied by the capture device. /// /// The timestamp is not synchronized with the system clock. AdapterUnsynced = 4, } #[cfg(test)] mod tests { use crate::{ capture::testmod::test_capture, raw::testmod::{as_pcap_t, geterr_expect, RAWMTX}, }; use super::*; #[test] fn test_from_device() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_create_context(); ctx.expect().return_once_st(move |_, _| pcap); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let result = Capture::from_device("some_device"); assert!(result.is_ok()); } #[test] fn test_from_device_error() { let _m = RAWMTX.lock(); let ctx = raw::pcap_create_context(); ctx.expect().return_once_st(|_, _| std::ptr::null_mut()); let result = Capture::from_device("some_device"); assert!(result.is_err()); } #[test] fn test_open() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_activate_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 0); let result = capture.open(); assert!(result.is_ok()); } #[test] fn test_open_error() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_activate_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| -1); let _err = geterr_expect(pcap); let result = capture.open(); assert!(result.is_err()); } #[test] fn test_timeout() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_timeout_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.timeout(5); } #[test] #[cfg(libpcap_1_2_1)] fn test_timstamp_type() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_tstamp_type_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.tstamp_type(TimestampType::Host); // For code coverage of the derive line. assert_ne!(TimestampType::Host, TimestampType::HostLowPrec); assert_ne!(TimestampType::Host, TimestampType::HostHighPrec); } #[test] fn test_promisc() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_promisc_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.promisc(true); } #[cfg(libpcap_1_5_0)] struct ImmediateModeExpect(raw::__pcap_set_immediate_mode::Context); #[cfg(all(windows, not(libpcap_1_5_0)))] struct ImmediateModeExpect(raw::__pcap_setmintocopy::Context); #[cfg(any(libpcap_1_5_0, windows))] fn immediate_mode_expect(pcap: *mut raw::pcap_t) -> ImmediateModeExpect { #[cfg(libpcap_1_5_0)] { let ctx = raw::pcap_set_immediate_mode_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); ImmediateModeExpect(ctx) } #[cfg(all(windows, not(libpcap_1_5_0)))] { let ctx = raw::pcap_setmintocopy_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); ImmediateModeExpect(ctx) } } #[test] #[cfg(any(libpcap_1_5_0, windows))] fn test_immediate_mode() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let _ctx = immediate_mode_expect(pcap); let capture = capture.immediate_mode(true); let _ctx = immediate_mode_expect(pcap); let _capture = capture.immediate_mode(false); } #[test] #[cfg(all(libpcap_1_5_3, target_os = "macos"))] fn test_want_pktap() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_want_pktap_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.want_pktap(true); } #[test] #[cfg(not(windows))] fn test_rfmon() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_rfmon_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.rfmon(true); } #[test] fn test_buffer_size() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_buffer_size_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.buffer_size(10); } #[test] #[cfg(libpcap_1_5_0)] fn test_precision() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_tstamp_precision_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.precision(Precision::Nano); } #[test] fn test_snaplen() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_set_snaplen_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.snaplen(10); } } pcap-2.2.0/src/capture/mod.rs000064400000000000000000000210461046102023000141320ustar 00000000000000pub mod activated; pub mod inactive; #[cfg(all(not(windows), feature = "capture-stream"))] pub mod selectable; use std::{ ffi::CString, marker::PhantomData, ptr::{self, NonNull}, }; #[cfg(windows)] use windows_sys::Win32::Foundation::HANDLE; use crate::{raw, Error}; /// Phantom type representing an inactive capture handle. pub enum Inactive {} /// Phantom type representing an active capture handle. pub enum Active {} /// Phantom type representing an offline capture handle, from a pcap dump file. /// Implements `Activated` because it behaves nearly the same as a live handle. pub enum Offline {} /// Phantom type representing a dead capture handle. This can be use to create /// new save files that are not generated from an active capture. /// Implements `Activated` because it behaves nearly the same as a live handle. pub enum Dead {} /// `Capture`s can be in different states at different times, and in these states they /// may or may not have particular capabilities. This trait is implemented by phantom /// types which allows us to punt these invariants to the type system to avoid runtime /// errors. pub trait Activated: State {} impl Activated for Active {} impl Activated for Offline {} impl Activated for Dead {} /// `Capture`s can be in different states at different times, and in these states they /// may or may not have particular capabilities. This trait is implemented by phantom /// types which allows us to punt these invariants to the type system to avoid runtime /// errors. pub trait State {} impl State for Inactive {} impl State for Active {} impl State for Offline {} impl State for Dead {} /// This is a pcap capture handle which is an abstraction over the `pcap_t` provided by pcap. /// There are many ways to instantiate and interact with a pcap handle, so phantom types are /// used to express these behaviors. /// /// **`Capture`** is created via `Capture::from_device()`. This handle is inactive, /// so you cannot (yet) obtain packets from it. However, you can configure things like the /// buffer size, snaplen, timeout, and promiscuity before you activate it. /// /// **`Capture`** is created by calling `.open()` on a `Capture`. This /// activates the capture handle, allowing you to get packets with `.next_packet()` or apply filters /// with `.filter()`. /// /// **`Capture`** is created via `Capture::from_file()`. This allows you to read a /// pcap format dump file as if you were opening an interface -- very useful for testing or /// analysis. /// /// **`Capture`** is created via `Capture::dead()`. This allows you to create a pcap /// format dump file without needing an active capture. /// /// # Example: /// /// ```no_run /// # use pcap::{Capture, Device}; /// let mut cap = Capture::from_device(Device::lookup().unwrap().unwrap()) // open the "default" interface /// .unwrap() // assume the device exists and we are authorized to open it /// .open() // activate the handle /// .unwrap(); // assume activation worked /// /// while let Ok(packet) = cap.next_packet() { /// println!("received packet! {:?}", packet); /// } /// ``` pub struct Capture { nonblock: bool, handle: NonNull, _marker: PhantomData, } // A Capture is safe to Send as it encapsulates the entire lifetime of `raw::pcap_t *`, but it is // not safe to Sync as libpcap does not promise thread-safe access to the same `raw::pcap_t *` from // multiple threads. unsafe impl Send for Capture {} impl From> for Capture { fn from(handle: NonNull) -> Self { Capture { nonblock: false, handle, _marker: PhantomData, } } } impl Capture { fn new_raw(path: Option<&str>, func: F) -> Result, Error> where F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t, { Error::with_errbuf(|err| { let handle = match path { None => func(ptr::null(), err), Some(path) => { let path = CString::new(path)?; func(path.as_ptr(), err) } }; Ok(Capture::from( NonNull::::new(handle).ok_or_else(|| unsafe { Error::new(err) })?, )) }) } pub fn is_nonblock(&self) -> bool { self.nonblock } pub fn as_ptr(&self) -> *mut raw::pcap_t { self.handle.as_ptr() } /// Set the minumum amount of data received by the kernel in a single call. /// /// Note that this value is set to 0 when the capture is set to immediate mode. You should not /// call `min_to_copy` on captures in immediate mode if you want them to stay in immediate mode. #[cfg(windows)] pub fn min_to_copy(self, to: i32) -> Capture { unsafe { raw::pcap_setmintocopy(self.handle.as_ptr(), to as _); } self } /// Get handle to the Capture context's internal Win32 event semaphore. /// /// # Safety /// /// The caller must ensure that the `Capture` context outlives the returned `HANDLE` since it is /// a kernel object owned by the `Capture`'s pcap context. #[cfg(windows)] pub unsafe fn get_event(&self) -> HANDLE { raw::pcap_getevent(self.handle.as_ptr()) } fn check_err(&self, success: bool) -> Result<(), Error> { if success { Ok(()) } else { Err(self.get_err()) } } fn get_err(&self) -> Error { unsafe { Error::new(raw::pcap_geterr(self.handle.as_ptr())) } } } impl Drop for Capture { fn drop(&mut self) { unsafe { raw::pcap_close(self.handle.as_ptr()) } } } #[repr(u32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] /// Timestamp resolution types /// /// Not all systems and interfaces will necessarily support all of these resolutions when doing /// live captures; all of them can be requested when reading a safefile. pub enum Precision { /// Use timestamps with microsecond precision. This is the default. Micro = 0, /// Use timestamps with nanosecond precision. Nano = 1, } // GRCOV_EXCL_START #[cfg(test)] pub mod testmod { use raw::testmod::RAWMTX; use super::*; pub struct TestCapture { pub capture: Capture, _close_ctx: raw::__pcap_close::Context, } pub fn test_capture(pcap: *mut raw::pcap_t) -> TestCapture { // Lock must be acquired by caller. assert!(RAWMTX.try_lock().is_err()); let ctx = raw::pcap_close_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); TestCapture { capture: Capture::::from(NonNull::new(pcap).unwrap()), _close_ctx: ctx, } } } // GRCOV_EXCL_STOP #[cfg(test)] mod tests { use crate::{ capture::testmod::test_capture, raw::testmod::{as_pcap_t, RAWMTX}, }; use super::*; #[test] fn test_capture_getters() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; assert!(!capture.is_nonblock()); assert_eq!(capture.as_ptr(), capture.handle.as_ptr()); } #[test] #[cfg(windows)] fn test_min_to_copy() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_setmintocopy_context(); ctx.expect() .withf_st(move |arg1, _| *arg1 == pcap) .return_once(|_, _| 0); let _capture = capture.min_to_copy(5); } #[test] #[cfg(windows)] fn test_get_event() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_getevent_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 5); let handle = unsafe { capture.get_event() }; assert_eq!(handle, 5); } #[test] fn test_precision() { assert_ne!(Precision::Micro, Precision::Nano); } } pcap-2.2.0/src/capture/selectable.rs000064400000000000000000000040631046102023000154560ustar 00000000000000use std::os::unix::io::{AsRawFd, RawFd}; use crate::{ capture::{Activated, Capture, State}, raw, Error, }; /// Newtype [`Capture`] wrapper that exposes `pcap_get_selectable_fd()`. pub struct SelectableCapture { inner: Capture, fd: RawFd, } impl SelectableCapture { pub fn new(capture: Capture) -> Result { let fd = unsafe { raw::pcap_get_selectable_fd(capture.as_ptr()) }; if fd == -1 { return Err(Error::InvalidRawFd); } Ok(Self { inner: capture, fd }) } pub fn get_inner_mut(&mut self) -> &mut Capture { &mut self.inner } } impl AsRawFd for SelectableCapture { fn as_raw_fd(&self) -> RawFd { self.fd } } #[cfg(test)] mod tests { use crate::{ capture::{testmod::test_capture, Active}, raw::testmod::{as_pcap_t, RAWMTX}, }; use super::*; #[test] fn test_selectable_capture() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_get_selectable_fd_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 5); let mut selectable = SelectableCapture::new(capture).unwrap(); assert!(!selectable.get_inner_mut().is_nonblock()); assert_eq!(selectable.as_raw_fd(), 5); } #[test] fn test_selectable_capture_error() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; let ctx = raw::pcap_get_selectable_fd_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| -1); let result = SelectableCapture::new(capture); assert!(result.is_err()); } } pcap-2.2.0/src/codec.rs000064400000000000000000000015731046102023000127700ustar 00000000000000use crate::packet::Packet; /// This trait is used to implement Stream and Iterator feature. /// This is almost like `map()`. /// // This is needed cause we don't have GaTs // Once GaTs are stable we could use them to implement better Iterator pub trait PacketCodec { type Item; fn decode(&mut self, packet: Packet<'_>) -> Self::Item; } // GRCOV_EXCL_START #[cfg(test)] pub mod testmod { use crate::packet::PacketHeader; use super::*; pub struct Codec; #[derive(Debug, PartialEq, Eq)] pub struct PacketOwned { pub header: PacketHeader, pub data: Box<[u8]>, } impl PacketCodec for Codec { type Item = PacketOwned; fn decode(&mut self, pkt: Packet) -> Self::Item { PacketOwned { header: *pkt.header, data: pkt.data.into(), } } } } // GRCOV_EXCL_STOP pcap-2.2.0/src/device.rs000064400000000000000000000503451046102023000131530ustar 00000000000000use std::{convert::TryFrom, net::IpAddr, ptr}; use bitflags::bitflags; #[cfg(target_os = "windows")] use windows_sys::Win32::Networking::WinSock; use crate::{ capture::{Active, Capture}, cstr_to_string, raw, Error, }; bitflags! { /// Network device flags. pub struct IfFlags: u32 { /// Set if the device is a loopback interface const LOOPBACK = raw::PCAP_IF_LOOPBACK; /// Set if the device is up const UP = raw::PCAP_IF_UP; /// Set if the device is running const RUNNING = raw::PCAP_IF_RUNNING; /// Set if the device is a wireless interface; this includes IrDA as well as radio-based /// networks such as IEEE 802.15.4 and IEEE 802.11, so it doesn't just mean Wi-Fi const WIRELESS = raw::PCAP_IF_WIRELESS; } } impl From for IfFlags { fn from(flags: u32) -> Self { IfFlags::from_bits_truncate(flags) } } #[derive(Debug, Clone, PartialEq, Eq)] /// Indication of whether the adapter is connected or not; for wireless interfaces, "connected" /// means "associated with a network". pub enum ConnectionStatus { /// It's unknown whether the adapter is connected or not Unknown, /// The adapter is connected Connected, /// The adapter is disconnected Disconnected, /// The notion of "connected" and "disconnected" don't apply to this interface; for example, it /// doesn't apply to a loopback device NotApplicable, } impl From for ConnectionStatus { fn from(flags: u32) -> Self { match flags & raw::PCAP_IF_CONNECTION_STATUS { raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN => ConnectionStatus::Unknown, raw::PCAP_IF_CONNECTION_STATUS_CONNECTED => ConnectionStatus::Connected, raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED => ConnectionStatus::Disconnected, raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE => ConnectionStatus::NotApplicable, // DeviceFlags::CONNECTION_STATUS should be a 2-bit mask which means that the four // values should cover all the possibilities. // GRCOV_EXCL_START _ => unreachable!(), // GRCOV_EXCL_STOP } } } #[derive(Debug, Clone)] pub struct DeviceFlags { pub if_flags: IfFlags, pub connection_status: ConnectionStatus, } impl From for DeviceFlags { fn from(flags: u32) -> Self { DeviceFlags { if_flags: flags.into(), connection_status: flags.into(), } } } impl DeviceFlags { pub fn empty() -> Self { DeviceFlags { if_flags: IfFlags::empty(), connection_status: ConnectionStatus::Unknown, } } pub fn contains(&self, if_flags: IfFlags) -> bool { self.if_flags.contains(if_flags) } pub fn is_loopback(&self) -> bool { self.contains(IfFlags::LOOPBACK) } pub fn is_up(&self) -> bool { self.contains(IfFlags::UP) } pub fn is_running(&self) -> bool { self.contains(IfFlags::RUNNING) } pub fn is_wireless(&self) -> bool { self.contains(IfFlags::WIRELESS) } } #[derive(Debug, Clone)] /// A network device name and pcap's description of it. pub struct Device { /// The name of the interface pub name: String, /// A textual description of the interface, if available pub desc: Option, /// Addresses associated with this interface pub addresses: Vec
, /// Interface flags pub flags: DeviceFlags, } impl Device { fn new( name: String, desc: Option, addresses: Vec
, flags: DeviceFlags, ) -> Device { Device { name, desc, addresses, flags, } } /// Opens a `Capture` on this device. pub fn open(self) -> Result, Error> { Capture::from_device(self)?.open() } /// Returns the default Device suitable for captures according to pcap_findalldevs, /// or an error from pcap. Note that there may be no suitable devices. pub fn lookup() -> Result, Error> { unsafe { Device::with_all_devs(|all_devs| { let dev = all_devs; Ok(if !dev.is_null() { Some(Device::try_from(&*dev)?) } else { None }) }) } } /// Returns a vector of `Device`s known by pcap via pcap_findalldevs. pub fn list() -> Result, Error> { unsafe { Device::with_all_devs(|all_devs| { let mut devices = vec![]; let mut dev = all_devs; while !dev.is_null() { devices.push(Device::try_from(&*dev)?); dev = (*dev).next; } Ok(devices) }) } } unsafe fn with_all_devs(func: F) -> Result where F: FnOnce(*mut raw::pcap_if_t) -> Result, { let all_devs = Error::with_errbuf(|err| { let mut all_devs: *mut raw::pcap_if_t = ptr::null_mut(); if raw::pcap_findalldevs(&mut all_devs, err) != 0 { return Err(Error::new(err)); } Ok(all_devs) })?; let result = func(all_devs); raw::pcap_freealldevs(all_devs); result } } impl From<&str> for Device { fn from(name: &str) -> Self { Device::new(name.into(), None, Vec::new(), DeviceFlags::empty()) } } impl TryFrom<&raw::pcap_if_t> for Device { type Error = Error; fn try_from(dev: &raw::pcap_if_t) -> Result { Ok(Device::new( unsafe { cstr_to_string(dev.name)?.ok_or(Error::InvalidString)? }, unsafe { cstr_to_string(dev.description)? }, unsafe { Address::new_vec(dev.addresses) }, DeviceFlags::from(dev.flags), )) } } #[derive(Debug, Clone)] /// Address information for an interface pub struct Address { /// The address pub addr: IpAddr, /// Network mask for this address pub netmask: Option, /// Broadcast address for this address pub broadcast_addr: Option, /// P2P destination address for this address pub dst_addr: Option, } impl Address { unsafe fn new_vec(mut ptr: *const raw::pcap_addr_t) -> Vec
{ let mut vec = Vec::new(); while !ptr.is_null() { if let Some(addr) = Address::new(ptr) { vec.push(addr); } ptr = (*ptr).next; } vec } unsafe fn new(ptr: *const raw::pcap_addr_t) -> Option
{ Self::convert_sockaddr((*ptr).addr).map(|addr| Address { addr, netmask: Self::convert_sockaddr((*ptr).netmask), broadcast_addr: Self::convert_sockaddr((*ptr).broadaddr), dst_addr: Self::convert_sockaddr((*ptr).dstaddr), }) } #[cfg(not(windows))] unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option { if ptr.is_null() { return None; } match (*ptr).sa_family as i32 { libc::AF_INET => { let ptr: *const libc::sockaddr_in = std::mem::transmute(ptr); Some(IpAddr::V4(u32::from_be((*ptr).sin_addr.s_addr).into())) } libc::AF_INET6 => { let ptr: *const libc::sockaddr_in6 = std::mem::transmute(ptr); Some(IpAddr::V6((*ptr).sin6_addr.s6_addr.into())) } _ => None, } } #[cfg(windows)] unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option { if ptr.is_null() { return None; } match (*ptr).sa_family as u32 { WinSock::AF_INET => { let ptr: *const WinSock::SOCKADDR_IN = std::mem::transmute(ptr); let addr: [u8; 4] = ((*ptr).sin_addr.S_un.S_addr).to_ne_bytes(); Some(IpAddr::from(addr)) } WinSock::AF_INET6 => { let ptr: *const WinSock::SOCKADDR_IN6 = std::mem::transmute(ptr); let addr = (*ptr).sin6_addr.u.Byte; Some(IpAddr::from(addr)) } _ => None, } } } #[cfg(test)] mod tests { use std::ffi::CString; use crate::raw::testmod::{as_pcap_t, RAWMTX}; use super::*; #[cfg(not(windows))] enum Sockaddr { SockaddrIn(libc::sockaddr_in), SockaddrIn6(libc::sockaddr_in6), } #[cfg(windows)] enum Sockaddr { SockaddrIn(WinSock::SOCKADDR_IN), SockaddrIn6(WinSock::SOCKADDR_IN6), } impl Sockaddr { fn as_mut_ptr(&mut self) -> *mut libc::sockaddr { match self { Sockaddr::SockaddrIn(ref mut sin) => sin as *mut _ as _, Sockaddr::SockaddrIn6(ref mut sin6) => sin6 as *mut _ as _, } } fn set_family(&mut self, family: u16) { // Annoyingly this differs between Linux (u16) and Mac (u8). #[cfg(not(windows))] let family = family as libc::sa_family_t; match self { Sockaddr::SockaddrIn(ref mut sin) => sin.sin_family = family, Sockaddr::SockaddrIn6(ref mut sin6) => sin6.sin6_family = family, } } } static IF1_NAME: &str = "if1"; static IF2_NAME: &str = "if2"; static IF1_DESC: &str = "if1 desc"; static IF2_DESC: &str = "if2 desc"; fn devs() -> Vec { let mut devs = vec![ raw::pcap_if_t { next: std::ptr::null_mut(), name: CString::new(IF1_NAME).unwrap().into_raw(), description: CString::new(IF1_DESC).unwrap().into_raw(), addresses: std::ptr::null_mut(), flags: (raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP), }, raw::pcap_if_t { next: std::ptr::null_mut(), name: CString::new(IF2_NAME).unwrap().into_raw(), description: CString::new(IF2_DESC).unwrap().into_raw(), addresses: std::ptr::null_mut(), flags: 0, }, ]; devs[0].next = &mut devs[1]; devs } trait InetAddressV4 { fn new() -> Self; fn set_addr(&mut self, addr: u32); } #[cfg(not(windows))] impl InetAddressV4 for libc::sockaddr_in { fn new() -> Self { let mut addr: Self = unsafe { std::mem::zeroed() }; addr.sin_family = libc::AF_INET as libc::sa_family_t; addr } fn set_addr(&mut self, addr: u32) { self.sin_addr.s_addr = addr; } } #[cfg(windows)] impl InetAddressV4 for WinSock::SOCKADDR_IN { fn new() -> Self { let mut addr: Self = unsafe { std::mem::zeroed() }; // The cast is only necessary due to a bug in windows_sys@v0.36.1 addr.sin_family = WinSock::AF_INET as u16; addr } fn set_addr(&mut self, addr: u32) { self.sin_addr.S_un.S_addr = addr; } } fn sockaddr_ipv4() -> Sockaddr { #[cfg(not(windows))] let mut addr: libc::sockaddr_in = InetAddressV4::new(); #[cfg(windows)] let mut addr: WinSock::SOCKADDR_IN = InetAddressV4::new(); addr.sin_port = 1075; addr.set_addr(0x0A000042_u32.to_be()); Sockaddr::SockaddrIn(addr) } trait InetAddressV6 { fn new() -> Self; fn set_octet(&mut self, index: usize, octet: u8); } #[cfg(not(windows))] impl InetAddressV6 for libc::sockaddr_in6 { fn new() -> Self { let mut addr: Self = unsafe { std::mem::zeroed() }; addr.sin6_family = libc::AF_INET6 as libc::sa_family_t; addr.sin6_addr.s6_addr[0] = 0xFE; addr.sin6_addr.s6_addr[1] = 0x80; addr } fn set_octet(&mut self, index: usize, octet: u8) { self.sin6_addr.s6_addr[index] = octet; } } #[cfg(windows)] impl InetAddressV6 for WinSock::SOCKADDR_IN6 { fn new() -> Self { let mut addr: Self = unsafe { std::mem::zeroed() }; // The cast is only necessary due to a bug in windows_sys@v0.36.1 addr.sin6_family = WinSock::AF_INET6 as u16; unsafe { addr.sin6_addr.u.Byte[0] = 0xFE; addr.sin6_addr.u.Byte[1] = 0x80; } addr } fn set_octet(&mut self, index: usize, octet: u8) { unsafe { self.sin6_addr.u.Byte[index] = octet }; } } fn sockaddr_ipv6() -> Sockaddr { #[cfg(not(windows))] let mut addr: libc::sockaddr_in6 = InetAddressV6::new(); #[cfg(windows)] let mut addr: WinSock::SOCKADDR_IN6 = InetAddressV6::new(); addr.sin6_port = 1075; addr.set_octet(15, 0x42); Sockaddr::SockaddrIn6(addr) } impl From<&mut Sockaddr> for raw::pcap_addr_t { fn from(value: &mut Sockaddr) -> Self { raw::pcap_addr_t { next: std::ptr::null_mut(), addr: value.as_mut_ptr(), netmask: std::ptr::null_mut(), broadaddr: std::ptr::null_mut(), dstaddr: std::ptr::null_mut(), } } } #[test] fn test_device_flags() { let flags = DeviceFlags::from( raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP | raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, ); assert!(flags.is_loopback()); assert!(flags.is_up()); assert!(flags.contains(IfFlags::LOOPBACK | IfFlags::UP)); assert!(!flags.is_running()); assert!(!flags.is_wireless()); assert_ne!(flags.connection_status, ConnectionStatus::Unknown); assert_ne!(flags.connection_status, ConnectionStatus::Connected); assert_ne!(flags.connection_status, ConnectionStatus::Disconnected); assert_eq!(flags.connection_status, ConnectionStatus::NotApplicable); assert!(!format!("{:?}", flags).is_empty()); } #[test] fn test_connection_status() { let flags = raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN; assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Unknown); let flags = raw::PCAP_IF_CONNECTION_STATUS_CONNECTED; assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Connected); let flags = raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED; assert_eq!( ConnectionStatus::from(flags), ConnectionStatus::Disconnected ); let flags = raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; assert_eq!( ConnectionStatus::from(flags), ConnectionStatus::NotApplicable ); } #[test] fn test_into_capture() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let ctx = raw::pcap_create_context(); ctx.expect().return_once_st(move |_, _| pcap); let ctx = raw::pcap_activate_context(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once(|_| 0); let ctx = raw::pcap_close_context(); ctx.expect() .withf_st(move |ptr| *ptr == pcap) .return_once(|_| {}); let device: Device = "device".into(); let _capture: Capture = device.clone().open().unwrap(); assert!(!format!("{:?}", device).is_empty()); } #[test] fn test_lookup() { let _m = RAWMTX.lock(); let ctx = raw::pcap_findalldevs_context(); ctx.expect().return_once_st(move |arg1, _| { unsafe { *arg1 = std::ptr::null_mut() }; 0 }); let ctx = raw::pcap_freealldevs_context(); ctx.expect().return_once(move |_| {}); let device = Device::lookup().unwrap(); assert!(device.is_none()); let mut devs = devs(); let mut addrs = sockaddr_ipv4(); let mut pcap_addr = (&mut addrs).into(); devs[0].addresses = &mut pcap_addr; let devs_ptr = devs.as_mut_ptr(); let ctx = raw::pcap_findalldevs_context(); ctx.checkpoint(); ctx.expect().return_once_st(move |arg1, _| { unsafe { *arg1 = devs_ptr }; 0 }); let ctx = raw::pcap_freealldevs_context(); ctx.checkpoint(); ctx.expect().return_once(move |_| {}); let device = Device::lookup().unwrap().unwrap(); assert_eq!(&device.name, IF1_NAME); assert_eq!(&device.desc.unwrap(), IF1_DESC); assert_eq!(device.addresses.len(), 1); assert!(device.addresses[0].addr.is_ipv4()); let ctx = raw::pcap_findalldevs_context(); ctx.checkpoint(); ctx.expect().return_once_st(move |_, _| -1); let ctx = raw::pcap_freealldevs_context(); ctx.checkpoint(); let result = Device::lookup(); assert!(result.is_err()); } #[test] fn test_list() { let _m = RAWMTX.lock(); let ctx = raw::pcap_findalldevs_context(); ctx.expect().return_once_st(move |arg1, _| { unsafe { *arg1 = std::ptr::null_mut() }; 0 }); let ctx = raw::pcap_freealldevs_context(); ctx.expect().return_once(move |_| {}); let devices = Device::list().unwrap(); assert!(devices.is_empty()); let mut devs = devs(); let mut ipv4s = sockaddr_ipv4(); let mut ipv6s = sockaddr_ipv6(); let mut pcap_addr: raw::pcap_addr_t = (&mut ipv4s).into(); let mut pcap_addr6: raw::pcap_addr_t = (&mut ipv6s).into(); pcap_addr.next = &mut pcap_addr6; devs[1].addresses = &mut pcap_addr; let devs_ptr = devs.as_mut_ptr(); let ctx = raw::pcap_findalldevs_context(); ctx.checkpoint(); ctx.expect().return_once_st(move |arg1, _| { unsafe { *arg1 = devs_ptr }; 0 }); let ctx = raw::pcap_freealldevs_context(); ctx.checkpoint(); ctx.expect().return_once(move |_| {}); let devices = Device::list().unwrap(); assert_eq!(devices.len(), devs.len()); assert_eq!(&devices[0].name, IF1_NAME); assert_eq!(devices[0].desc.as_ref().unwrap(), IF1_DESC); assert_eq!(devices[0].addresses.len(), 0); assert_eq!(&devices[1].name, IF2_NAME); assert_eq!(devices[1].desc.as_ref().unwrap(), IF2_DESC); assert_eq!(devices[1].addresses.len(), 2); assert!(devices[1].addresses[0].addr.is_ipv4()); assert!(devices[1].addresses[1].addr.is_ipv6()); let ctx = raw::pcap_findalldevs_context(); ctx.checkpoint(); ctx.expect().return_once_st(move |_, _| -1); let ctx = raw::pcap_freealldevs_context(); ctx.checkpoint(); let result = Device::list(); assert!(result.is_err()); } #[test] fn test_address_ipv4() { let mut addr = sockaddr_ipv4(); let pcap_addr: raw::pcap_addr_t = (&mut addr).into(); let address = unsafe { Address::new(&pcap_addr) }.unwrap(); assert!(address.addr.is_ipv4()); assert_eq!(address.addr.to_string(), "10.0.0.66"); assert!(address.netmask.is_none()); assert!(address.broadcast_addr.is_none()); assert!(address.dst_addr.is_none()); assert!(!format!("{:?}", address).is_empty()); } #[test] fn test_address_family() { let mut addr = sockaddr_ipv4(); #[cfg(not(windows))] addr.set_family(libc::AF_IPX as u16); #[cfg(windows)] addr.set_family(WinSock::AF_IPX); let pcap_addr: raw::pcap_addr_t = (&mut addr).into(); let address = unsafe { Address::new(&pcap_addr) }; assert!(address.is_none()); } #[test] fn test_address_ipv6() { let mut addr = sockaddr_ipv6(); let pcap_addr: raw::pcap_addr_t = (&mut addr).into(); let address = unsafe { Address::new(&pcap_addr) }.unwrap(); assert!(address.addr.is_ipv6()); assert_eq!(address.addr.to_string(), "fe80::42"); assert!(address.netmask.is_none()); assert!(address.broadcast_addr.is_none()); assert!(address.dst_addr.is_none()); assert!(!format!("{:?}", address).is_empty()); } } pcap-2.2.0/src/lib.rs000064400000000000000000000223071046102023000124570ustar 00000000000000//! pcap is a packet capture library available on Linux, Windows and Mac. This //! crate supports creating and configuring capture contexts, sniffing packets, //! sending packets to interfaces, listing devices, and recording packet captures //! to pcap-format dump files. //! //! # Capturing packets //! The easiest way to open an active capture handle and begin sniffing is to //! use `.open()` on a `Device`. You can obtain the "default" device using //! `Device::lookup()`, or you can obtain the device(s) you need via `Device::list()`. //! //! ```no_run //! use pcap::Device; //! //! let mut cap = Device::lookup().unwrap().unwrap().open().unwrap(); //! //! while let Ok(packet) = cap.next_packet() { //! println!("received packet! {:?}", packet); //! } //! //! ``` //! //! `Capture`'s `.next_packet()` will produce a `Packet` which can be dereferenced to access the //! `&[u8]` packet contents. //! //! # Custom configuration //! //! You may want to configure the `timeout`, `snaplen` or other parameters for the capture //! handle. In this case, use `Capture::from_device()` to obtain a `Capture`, and //! proceed to configure the capture handle. When you're finished, run `.open()` on it to //! turn it into a `Capture`. //! //! ```no_run //! use pcap::{Device, Capture}; //! //! let main_device = Device::lookup().unwrap().unwrap(); //! let mut cap = Capture::from_device(main_device).unwrap() //! .promisc(true) //! .snaplen(5000) //! .open().unwrap(); //! //! while let Ok(packet) = cap.next_packet() { //! println!("received packet! {:?}", packet); //! } //! ``` //! //! # Abstracting over different capture types //! //! You can abstract over live captures (`Capture`) and file captures //! (`Capture`) using generics and the [`Activated`] trait, for example: //! //! ``` //! use pcap::{Activated, Capture}; //! //! fn read_packets(mut capture: Capture) { //! while let Ok(packet) = capture.next_packet() { //! println!("received packet! {:?}", packet); //! } //! } //! ``` use std::ffi::{self, CStr}; use std::fmt; use self::Error::*; mod capture; mod codec; mod device; mod linktype; mod packet; #[cfg(not(windows))] pub use capture::activated::open_raw_fd; pub use capture::{ activated::{iterator::PacketIter, BpfInstruction, BpfProgram, Direction, Savefile, Stat}, inactive::TimestampType, {Activated, Active, Capture, Dead, Inactive, Offline, Precision, State}, }; pub use codec::PacketCodec; pub use device::{Address, ConnectionStatus, Device, DeviceFlags, IfFlags}; pub use linktype::Linktype; pub use packet::{Packet, PacketHeader}; #[deprecated(note = "Renamed to TimestampType")] /// An old name for `TimestampType`, kept around for backward-compatibility. pub type TstampType = TimestampType; mod raw; #[cfg(windows)] pub mod sendqueue; #[cfg(feature = "capture-stream")] mod stream; #[cfg(feature = "capture-stream")] pub use stream::PacketStream; /// An error received from pcap #[derive(Debug, PartialEq, Eq)] pub enum Error { /// The underlying library returned invalid UTF-8 MalformedError(std::str::Utf8Error), /// The underlying library returned a null string InvalidString, /// The unerlying library returned an error PcapError(String), /// The linktype was invalid or unknown InvalidLinktype, /// The timeout expired while reading from a live capture TimeoutExpired, /// No more packets to read from the file NoMorePackets, /// Must be in non-blocking mode to function NonNonBlock, /// There is not sufficent memory to create a dead capture InsufficientMemory, /// An invalid input string (internal null) InvalidInputString, /// An IO error occurred IoError(std::io::ErrorKind), #[cfg(not(windows))] /// An invalid raw file descriptor was provided InvalidRawFd, /// Errno error ErrnoError(errno::Errno), /// Buffer size overflows capacity BufferOverflow, } impl Error { unsafe fn new(ptr: *const libc::c_char) -> Error { match cstr_to_string(ptr) { Err(e) => e as Error, Ok(string) => PcapError(string.unwrap_or_default()), } } fn with_errbuf(func: F) -> Result where F: FnOnce(*mut libc::c_char) -> Result, { let mut errbuf = [0i8; 256]; func(errbuf.as_mut_ptr() as _) } } unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result, Error> { let string = if ptr.is_null() { None } else { Some(CStr::from_ptr(ptr as _).to_str()?.to_owned()) }; Ok(string) } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {}", e), InvalidString => write!(f, "libpcap returned a null string"), PcapError(ref e) => write!(f, "libpcap error: {}", e), InvalidLinktype => write!(f, "invalid or unknown linktype"), TimeoutExpired => write!(f, "timeout expired while reading from a live capture"), NonNonBlock => write!(f, "must be in non-blocking mode to function"), NoMorePackets => write!(f, "no more packets to read from the file"), InsufficientMemory => write!(f, "insufficient memory"), InvalidInputString => write!(f, "invalid input string (internal null)"), IoError(ref e) => write!(f, "io error occurred: {:?}", e), #[cfg(not(windows))] InvalidRawFd => write!(f, "invalid raw file descriptor provided"), ErrnoError(ref e) => write!(f, "libpcap os errno: {}", e), BufferOverflow => write!(f, "buffer size too large"), } } } // Using description is deprecated. Remove in next version. impl std::error::Error for Error { fn description(&self) -> &str { match *self { MalformedError(..) => "libpcap returned invalid UTF-8", PcapError(..) => "libpcap FFI error", InvalidString => "libpcap returned a null string", InvalidLinktype => "invalid or unknown linktype", TimeoutExpired => "timeout expired while reading from a live capture", NonNonBlock => "must be in non-blocking mode to function", NoMorePackets => "no more packets to read from the file", InsufficientMemory => "insufficient memory", InvalidInputString => "invalid input string (internal null)", IoError(..) => "io error occurred", #[cfg(not(windows))] InvalidRawFd => "invalid raw file descriptor provided", ErrnoError(..) => "internal error, providing errno", BufferOverflow => "buffer size too large", } } fn cause(&self) -> Option<&dyn std::error::Error> { match *self { MalformedError(ref e) => Some(e), _ => None, } } } impl From for Error { fn from(_: ffi::NulError) -> Error { InvalidInputString } } impl From for Error { fn from(obj: std::str::Utf8Error) -> Error { MalformedError(obj) } } impl From for Error { fn from(obj: std::io::Error) -> Error { obj.kind().into() } } impl From for Error { fn from(obj: std::io::ErrorKind) -> Error { IoError(obj) } } #[cfg(test)] mod tests { use std::error::Error as StdError; use std::{ffi::CString, io}; use super::*; #[test] fn test_error_invalid_utf8() { let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f]; let error = unsafe { Error::new(&bytes as *const _ as _) }; assert!(matches!(error, Error::MalformedError(_))); } #[test] fn test_error_null() { let error = unsafe { Error::new(std::ptr::null()) }; assert_eq!(error, Error::PcapError("".to_string())); } #[test] #[allow(deprecated)] fn test_errors() { let mut errors: Vec = vec![]; let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f]; let cstr = unsafe { CStr::from_ptr(&bytes as *const _ as _) }; errors.push(cstr.to_str().unwrap_err().into()); errors.push(Error::InvalidString); errors.push(Error::PcapError("git rekt".to_string())); errors.push(Error::InvalidLinktype); errors.push(Error::TimeoutExpired); errors.push(Error::NoMorePackets); errors.push(Error::NonNonBlock); errors.push(Error::InsufficientMemory); errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into()); errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into()); #[cfg(not(windows))] errors.push(Error::InvalidRawFd); errors.push(Error::ErrnoError(errno::Errno(125))); errors.push(Error::BufferOverflow); for error in errors.iter() { assert!(!error.to_string().is_empty()); assert!(!error.description().is_empty()); match error { Error::MalformedError(_) => assert!(error.cause().is_some()), _ => assert!(error.cause().is_none()), } } } } pcap-2.2.0/src/linktype.rs000064400000000000000000000201061046102023000135430ustar 00000000000000use std::ffi::CString; use crate::{cstr_to_string, raw, Error}; /// This is a datalink link type. /// /// As an example, `Linktype(1)` is ethernet. A full list of linktypes is available /// [here](http://www.tcpdump.org/linktypes.html). The const bellow are not exhaustive. /// ```rust /// use pcap::Linktype; /// /// let lt = Linktype(1); /// assert_eq!(Linktype::ETHERNET, lt); /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Linktype(pub i32); impl Linktype { /// Gets the name of the link type, such as EN10MB pub fn get_name(&self) -> Result { unsafe { cstr_to_string(raw::pcap_datalink_val_to_name(self.0)) }? .ok_or(Error::InvalidLinktype) } /// Gets the description of a link type. pub fn get_description(&self) -> Result { unsafe { cstr_to_string(raw::pcap_datalink_val_to_description(self.0)) }? .ok_or(Error::InvalidLinktype) } /// Gets the linktype from a name string pub fn from_name(name: &str) -> Result { let name = CString::new(name)?; let val = unsafe { raw::pcap_datalink_name_to_val(name.as_ptr()) }; if val == -1 { return Err(Error::InvalidLinktype); } Ok(Linktype(val)) } pub const NULL: Self = Self(0); pub const ETHERNET: Self = Self(1); pub const AX25: Self = Self(3); pub const IEEE802_5: Self = Self(6); pub const ARCNET_BSD: Self = Self(7); pub const SLIP: Self = Self(8); pub const PPP: Self = Self(9); pub const FDDI: Self = Self(10); pub const PPP_HDLC: Self = Self(50); pub const PPP_ETHER: Self = Self(51); pub const ATM_RFC1483: Self = Self(100); pub const RAW: Self = Self(101); pub const C_HDLC: Self = Self(104); pub const IEEE802_11: Self = Self(105); pub const FRELAY: Self = Self(107); pub const LOOP: Self = Self(108); pub const LINUX_SLL: Self = Self(113); pub const LTALK: Self = Self(114); pub const PFLOG: Self = Self(117); pub const IEEE802_11_PRISM: Self = Self(119); pub const IP_OVER_FC: Self = Self(122); pub const SUNATM: Self = Self(123); pub const IEEE802_11_RADIOTAP: Self = Self(127); pub const ARCNET_LINUX: Self = Self(129); pub const APPLE_IP_OVER_IEEE1394: Self = Self(138); pub const MTP2_WITH_PHDR: Self = Self(139); pub const MTP2: Self = Self(140); pub const MTP3: Self = Self(141); pub const SCCP: Self = Self(142); pub const DOCSIS: Self = Self(143); pub const LINUX_IRDA: Self = Self(144); pub const USER0: Self = Self(147); pub const USER1: Self = Self(148); pub const USER2: Self = Self(149); pub const USER3: Self = Self(150); pub const USER4: Self = Self(151); pub const USER5: Self = Self(152); pub const USER6: Self = Self(153); pub const USER7: Self = Self(154); pub const USER8: Self = Self(155); pub const USER9: Self = Self(156); pub const USER10: Self = Self(157); pub const USER11: Self = Self(158); pub const USER12: Self = Self(159); pub const USER13: Self = Self(160); pub const USER14: Self = Self(161); pub const USER15: Self = Self(162); pub const IEEE802_11_AVS: Self = Self(163); pub const BACNET_MS_TP: Self = Self(165); pub const PPP_PPPD: Self = Self(166); pub const GPRS_LLC: Self = Self(169); pub const GPF_T: Self = Self(170); pub const GPF_F: Self = Self(171); pub const LINUX_LAPD: Self = Self(177); pub const MFR: Self = Self(182); pub const BLUETOOTH_HCI_H4: Self = Self(187); pub const USB_LINUX: Self = Self(189); pub const PPI: Self = Self(192); pub const IEEE802_15_4_WITHFCS: Self = Self(195); pub const SITA: Self = Self(196); pub const ERF: Self = Self(197); pub const BLUETOOTH_HCI_H4_WITH_PHDR: Self = Self(201); pub const AX25_KISS: Self = Self(202); pub const LAPD: Self = Self(203); pub const PPP_WITH_DIR: Self = Self(204); pub const C_HDLC_WITH_DIR: Self = Self(205); pub const FRELAY_WITH_DIR: Self = Self(206); pub const LAPB_WITH_DIR: Self = Self(207); pub const IPMB_LINUX: Self = Self(209); pub const IEEE802_15_4_NONASK_PHY: Self = Self(215); pub const USB_LINUX_MMAPPED: Self = Self(220); pub const FC_2: Self = Self(224); pub const FC_2_WITH_FRAME_DELIMS: Self = Self(225); pub const IPNET: Self = Self(226); pub const CAN_SOCKETCAN: Self = Self(227); pub const IPV4: Self = Self(228); pub const IPV6: Self = Self(229); pub const IEEE802_15_4_NOFCS: Self = Self(230); pub const DBUS: Self = Self(231); pub const DVB_CI: Self = Self(235); pub const MUX27010: Self = Self(236); pub const STANAG_5066_D_PDU: Self = Self(237); pub const NFLOG: Self = Self(239); pub const NETANALYZER: Self = Self(240); pub const NETANALYZER_TRANSPARENT: Self = Self(241); pub const IPOIB: Self = Self(242); pub const MPEG_2_TS: Self = Self(243); pub const NG40: Self = Self(244); pub const NFC_LLCP: Self = Self(245); pub const INFINIBAND: Self = Self(247); pub const SCTP: Self = Self(248); pub const USBPCAP: Self = Self(249); pub const RTAC_SERIAL: Self = Self(250); pub const BLUETOOTH_LE_LL: Self = Self(251); pub const NETLINK: Self = Self(253); pub const BLUETOOTH_LINUX_MONITOR: Self = Self(254); pub const BLUETOOTH_BREDR_BB: Self = Self(255); pub const BLUETOOTH_LE_LL_WITH_PHDR: Self = Self(256); pub const PROFIBUS_DL: Self = Self(257); pub const PKTAP: Self = Self(258); pub const EPON: Self = Self(259); pub const IPMI_HPM_2: Self = Self(260); pub const ZWAVE_R1_R2: Self = Self(261); pub const ZWAVE_R3: Self = Self(262); pub const WATTSTOPPER_DLM: Self = Self(263); pub const ISO_14443: Self = Self(264); pub const RDS: Self = Self(265); pub const USB_DARWIN: Self = Self(266); pub const SDLC: Self = Self(268); pub const LORATAP: Self = Self(270); pub const VSOCK: Self = Self(271); pub const NORDIC_BLE: Self = Self(272); pub const DOCSIS31_XRA31: Self = Self(273); pub const ETHERNET_MPACKET: Self = Self(274); pub const DISPLAYPORT_AUX: Self = Self(275); pub const LINUX_SLL2: Self = Self(276); pub const OPENVIZSLA: Self = Self(278); pub const EBHSCR: Self = Self(279); pub const VPP_DISPATCH: Self = Self(280); pub const DSA_TAG_BRCM: Self = Self(281); pub const DSA_TAG_BRCM_PREPEND: Self = Self(282); pub const IEEE802_15_4_TAP: Self = Self(283); pub const DSA_TAG_DSA: Self = Self(284); pub const DSA_TAG_EDSA: Self = Self(285); pub const ELEE: Self = Self(286); pub const Z_WAVE_SERIAL: Self = Self(287); pub const USB_2_0: Self = Self(288); pub const ATSC_ALP: Self = Self(289); } #[cfg(test)] mod tests { use crate::raw::testmod::RAWMTX; use super::*; #[test] fn test_get_name() { let _m = RAWMTX.lock(); let name = "datalink name"; let cstr = CString::new(name).unwrap(); let ctx = raw::pcap_datalink_val_to_name_context(); ctx.expect().return_once(|_| cstr.into_raw()); let linktype_name = Linktype::ARCNET_LINUX.get_name().unwrap(); assert_eq!(&linktype_name, name); } #[test] fn test_get_description() { let _m = RAWMTX.lock(); let desc = "datalink description"; let cstr = CString::new(desc).unwrap(); let ctx = raw::pcap_datalink_val_to_description_context(); ctx.expect().return_once(|_| cstr.into_raw()); let linktype_name = Linktype::ARCNET_LINUX.get_description().unwrap(); assert_eq!(&linktype_name, desc); } #[test] fn test_from_name() { let _m = RAWMTX.lock(); let ctx = raw::pcap_datalink_name_to_val_context(); ctx.expect().return_once(|_| 7); let linktype = Linktype::from_name("git rekt scrub").unwrap(); assert_eq!(linktype, Linktype::ARCNET_BSD); let ctx = raw::pcap_datalink_name_to_val_context(); ctx.checkpoint(); ctx.expect().return_once(|_| -1); let err = Linktype::from_name("git rekt scrub").unwrap_err(); assert_eq!(err, Error::InvalidLinktype); } } pcap-2.2.0/src/packet.rs000064400000000000000000000046471046102023000131670ustar 00000000000000use std::{fmt, ops::Deref}; /// Represents a packet returned from pcap. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Packet<'a> { /// The packet header provided by pcap, including the timeval, captured length, and packet /// length pub header: &'a PacketHeader, /// The captured packet data pub data: &'a [u8], } impl<'a> Packet<'a> { #[doc(hidden)] pub fn new(header: &'a PacketHeader, data: &'a [u8]) -> Packet<'a> { Packet { header, data } } } impl<'b> Deref for Packet<'b> { type Target = [u8]; fn deref(&self) -> &[u8] { self.data } } #[repr(C)] #[derive(Copy, Clone)] /// Represents a packet header provided by pcap, including the timeval, caplen and len. pub struct PacketHeader { /// The time when the packet was captured pub ts: libc::timeval, /// The number of bytes of the packet that are available from the capture pub caplen: u32, /// The length of the packet, in bytes (which might be more than the number of bytes available /// from the capture, if the length of the packet is larger than the maximum number of bytes to /// capture) pub len: u32, } impl fmt::Debug for PacketHeader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "PacketHeader {{ ts: {}.{:06}, caplen: {}, len: {} }}", self.ts.tv_sec, self.ts.tv_usec, self.caplen, self.len ) } } impl PartialEq for PacketHeader { fn eq(&self, rhs: &PacketHeader) -> bool { self.ts.tv_sec == rhs.ts.tv_sec && self.ts.tv_usec == rhs.ts.tv_usec && self.caplen == rhs.caplen && self.len == rhs.len } } impl Eq for PacketHeader {} #[cfg(test)] mod tests { use crate::raw; use super::*; static HEADER: PacketHeader = PacketHeader { ts: libc::timeval { tv_sec: 5, tv_usec: 50, }, caplen: 5, len: 9, }; #[test] fn test_packet_header_size() { use std::mem::size_of; assert_eq!(size_of::(), size_of::()); } #[test] fn test_packet_header_clone() { // For code coverag purposes. #[allow(clippy::clone_on_copy)] let header_clone = HEADER.clone(); assert_eq!(header_clone, HEADER); } #[test] fn test_packet_header_display() { assert!(!format!("{:?}", HEADER).is_empty()); } } pcap-2.2.0/src/raw.rs000064400000000000000000000307701046102023000125050ustar 00000000000000// GRCOV_EXCL_START #![allow(dead_code)] #![allow(non_camel_case_types)] use libc::{c_char, c_int, c_uchar, c_uint, c_ushort, sockaddr, timeval, FILE}; #[cfg(test)] use mockall::automock; pub const PCAP_IF_LOOPBACK: u32 = 0x00000001; pub const PCAP_IF_UP: u32 = 0x00000002; pub const PCAP_IF_RUNNING: u32 = 0x00000004; pub const PCAP_IF_WIRELESS: u32 = 0x00000008; pub const PCAP_IF_CONNECTION_STATUS: u32 = 0x00000030; pub const PCAP_IF_CONNECTION_STATUS_UNKNOWN: u32 = 0x00000000; pub const PCAP_IF_CONNECTION_STATUS_CONNECTED: u32 = 0x00000010; pub const PCAP_IF_CONNECTION_STATUS_DISCONNECTED: u32 = 0x00000020; pub const PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE: u32 = 0x00000030; #[repr(C)] #[derive(Copy, Clone)] pub struct bpf_program { pub bf_len: c_uint, pub bf_insns: *mut bpf_insn, } #[repr(C)] #[derive(Copy, Clone)] pub struct bpf_insn { pub code: c_ushort, pub jt: c_uchar, pub jf: c_uchar, pub k: c_uint, } pub enum pcap_t {} pub enum pcap_dumper_t {} #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_file_header { pub magic: c_uint, pub version_major: c_ushort, pub version_minor: c_ushort, pub thiszone: c_int, pub sigfigs: c_uint, pub snaplen: c_uint, pub linktype: c_uint, } pub type pcap_direction_t = c_uint; pub const PCAP_D_INOUT: pcap_direction_t = 0; pub const PCAP_D_IN: pcap_direction_t = 1; pub const PCAP_D_OUT: pcap_direction_t = 2; #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_pkthdr { pub ts: timeval, pub caplen: c_uint, pub len: c_uint, } #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_stat { pub ps_recv: c_uint, pub ps_drop: c_uint, pub ps_ifdrop: c_uint, } #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_if_t { pub next: *mut pcap_if_t, pub name: *mut c_char, pub description: *mut c_char, pub addresses: *mut pcap_addr_t, pub flags: c_uint, } #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_addr_t { pub next: *mut pcap_addr_t, pub addr: *mut sockaddr, pub netmask: *mut sockaddr, pub broadaddr: *mut sockaddr, pub dstaddr: *mut sockaddr, } #[cfg(windows)] #[repr(C)] #[derive(Copy, Clone)] pub struct pcap_send_queue { pub maxlen: c_uint, pub len: c_uint, pub buffer: *mut c_char, } // This is not Option, pcap functions do not check if the handler is null so it is wrong to // pass them Option::::None. pub type pcap_handler = extern "C" fn(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar) -> (); #[cfg_attr(test, automock)] pub mod ffi { use super::*; extern "C" { // [OBSOLETE] pub fn pcap_lookupdev(arg1: *mut c_char) -> *mut c_char; // pub fn pcap_lookupnet(arg1: *const c_char, arg2: *mut c_uint, arg3: *mut c_uint, // arg4: *mut c_char) -> c_int; pub fn pcap_create(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t; pub fn pcap_set_snaplen(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_set_promisc(arg1: *mut pcap_t, arg2: c_int) -> c_int; // pub fn pcap_can_set_rfmon(arg1: *mut pcap_t) -> c_int; pub fn pcap_set_timeout(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_set_buffer_size(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_activate(arg1: *mut pcap_t) -> c_int; // pub fn pcap_open_live(arg1: *const c_char, arg2: c_int, arg3: c_int, arg4: c_int, // arg5: *mut c_char) -> *mut pcap_t; pub fn pcap_open_dead(arg1: c_int, arg2: c_int) -> *mut pcap_t; pub fn pcap_open_offline(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t; pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut c_char) -> *mut pcap_t; pub fn pcap_close(arg1: *mut pcap_t); pub fn pcap_loop( arg1: *mut pcap_t, arg2: c_int, arg3: pcap_handler, arg4: *mut c_uchar, ) -> c_int; // pub fn pcap_dispatch(arg1: *mut pcap_t, arg2: c_int, arg3: pcap_handler, // arg4: *mut c_uchar)-> c_int; // pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut pcap_pkthdr) -> *const c_uchar; pub fn pcap_next_ex( arg1: *mut pcap_t, arg2: *mut *mut pcap_pkthdr, arg3: *mut *const c_uchar, ) -> c_int; pub fn pcap_breakloop(arg1: *mut pcap_t); pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut pcap_stat) -> c_int; pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut bpf_program) -> c_int; pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) -> c_int; // pub fn pcap_getnonblock(arg1: *mut pcap_t, arg2: *mut c_char) -> c_int; pub fn pcap_setnonblock(arg1: *mut pcap_t, arg2: c_int, arg3: *mut c_char) -> c_int; pub fn pcap_sendpacket(arg1: *mut pcap_t, arg2: *const c_uchar, arg3: c_int) -> c_int; // pub fn pcap_statustostr(arg1: c_int) -> *const c_char; // pub fn pcap_strerror(arg1: c_int) -> *const c_char; pub fn pcap_geterr(arg1: *mut pcap_t) -> *mut c_char; // pub fn pcap_perror(arg1: *mut pcap_t, arg2: *mut c_char); pub fn pcap_compile( arg1: *mut pcap_t, arg2: *mut bpf_program, arg3: *const c_char, arg4: c_int, arg5: c_uint, ) -> c_int; // pub fn pcap_compile_nopcap(arg1: c_int, arg2: c_int, arg3: *mut bpf_program, // arg4: *const c_char, arg5: c_int, arg6: c_uint) -> c_int; pub fn pcap_freecode(arg1: *mut bpf_program); pub fn pcap_offline_filter( arg1: *const bpf_program, arg2: *const pcap_pkthdr, arg3: *const c_uchar, ) -> c_int; pub fn pcap_datalink(arg1: *mut pcap_t) -> c_int; // pub fn pcap_datalink_ext(arg1: *mut pcap_t) -> c_int; pub fn pcap_list_datalinks(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int; pub fn pcap_set_datalink(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_free_datalinks(arg1: *mut c_int); pub fn pcap_datalink_name_to_val(arg1: *const c_char) -> c_int; pub fn pcap_datalink_val_to_name(arg1: c_int) -> *const c_char; pub fn pcap_datalink_val_to_description(arg1: c_int) -> *const c_char; // pub fn pcap_snapshot(arg1: *mut pcap_t) -> c_int; // pub fn pcap_is_swapped(arg1: *mut pcap_t) -> c_int; pub fn pcap_major_version(arg1: *mut pcap_t) -> c_int; pub fn pcap_minor_version(arg1: *mut pcap_t) -> c_int; // pub fn pcap_file(arg1: *mut pcap_t) -> *mut FILE; pub fn pcap_fileno(arg1: *mut pcap_t) -> c_int; pub fn pcap_dump_open(arg1: *mut pcap_t, arg2: *const c_char) -> *mut pcap_dumper_t; pub fn pcap_dump_fopen(arg1: *mut pcap_t, fp: *mut FILE) -> *mut pcap_dumper_t; // pub fn pcap_dump_file(arg1: *mut pcap_dumper_t) -> *mut FILE; // pub fn pcap_dump_ftell(arg1: *mut pcap_dumper_t) -> c_long; pub fn pcap_dump_flush(arg1: *mut pcap_dumper_t) -> c_int; pub fn pcap_dump_close(arg1: *mut pcap_dumper_t); pub fn pcap_dump(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar); pub fn pcap_findalldevs(arg1: *mut *mut pcap_if_t, arg2: *mut c_char) -> c_int; pub fn pcap_freealldevs(arg1: *mut pcap_if_t); // pub fn pcap_lib_version() -> *const c_char; // pub fn bpf_image(arg1: *const bpf_insn, arg2: c_int) -> *mut c_char; // pub fn bpf_dump(arg1: *const bpf_program, arg2: c_int); pub fn pcap_get_selectable_fd(arg1: *mut pcap_t) -> c_int; } #[cfg(libpcap_1_2_1)] extern "C" { // pub fn pcap_free_tstamp_types(arg1: *mut c_int) -> (); // pub fn pcap_list_tstamp_types(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int; // pub fn pcap_tstamp_type_name_to_val(arg1: *const c_char) -> c_int; // pub fn pcap_tstamp_type_val_to_description(arg1: c_int) -> *const c_char; // pub fn pcap_tstamp_type_val_to_name(arg1: c_int) -> *const c_char; pub fn pcap_set_tstamp_type(arg1: *mut pcap_t, arg2: c_int) -> c_int; } #[cfg(libpcap_1_5_0)] extern "C" { pub fn pcap_fopen_offline_with_tstamp_precision( arg1: *mut FILE, arg2: c_uint, arg3: *mut c_char, ) -> *mut pcap_t; // pub fn pcap_get_tstamp_precision(arg1: *mut pcap_t) -> c_int; pub fn pcap_open_dead_with_tstamp_precision( arg1: c_int, arg2: c_int, arg3: c_uint, ) -> *mut pcap_t; pub fn pcap_open_offline_with_tstamp_precision( arg1: *const c_char, arg2: c_uint, arg3: *mut c_char, ) -> *mut pcap_t; pub fn pcap_set_immediate_mode(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_set_tstamp_precision(arg1: *mut pcap_t, arg2: c_int) -> c_int; } #[cfg(libpcap_1_7_2)] extern "C" { pub fn pcap_dump_open_append(arg1: *mut pcap_t, arg2: *const c_char) -> *mut pcap_dumper_t; } #[cfg(libpcap_1_9_0)] extern "C" { // pcap_bufsize // pcap_createsrcstr // pcap_dump_ftell64 // pcap_findalldevs_ex // pcap_get_required_select_timeout // pcap_open // pcap_parsesrcstr // pcap_remoteact_accept // pcap_remoteact_cleanup // pcap_remoteact_close // pcap_remoteact_list // pcap_set_protocol_linux // pcap_setsampling } #[cfg(libpcap_1_9_1)] extern "C" { // pcap_datalink_val_to_description_or_dlt } } #[cfg(not(windows))] #[cfg_attr(test, automock)] pub mod ffi_unix { use super::*; #[link(name = "pcap")] extern "C" { // pub fn pcap_inject(arg1: *mut pcap_t, arg2: *const c_void, arg3: size_t) -> c_int; pub fn pcap_set_rfmon(arg1: *mut pcap_t, arg2: c_int) -> c_int; } } #[cfg(target_os = "macos")] #[cfg_attr(test, automock)] pub mod ffi_macos { use super::*; #[cfg(libpcap_1_5_3)] extern "C" { pub fn pcap_set_want_pktap(arg1: *mut pcap_t, arg2: c_int) -> c_int; } } #[cfg(windows)] #[cfg_attr(test, automock)] pub mod ffi_windows { use windows_sys::Win32::Foundation::HANDLE; use super::*; pub const WINPCAP_MINTOCOPY_DEFAULT: c_int = 16000; #[link(name = "wpcap")] extern "C" { pub fn pcap_setmintocopy(arg1: *mut pcap_t, arg2: c_int) -> c_int; pub fn pcap_getevent(p: *mut pcap_t) -> HANDLE; pub fn pcap_sendqueue_alloc(memsize: c_uint) -> *mut pcap_send_queue; pub fn pcap_sendqueue_destroy(queue: *mut pcap_send_queue); pub fn pcap_sendqueue_queue( queue: *mut pcap_send_queue, pkt_header: *const pcap_pkthdr, pkt_data: *const c_uchar, ) -> c_int; pub fn pcap_sendqueue_transmit( p: *mut pcap_t, queue: *mut pcap_send_queue, sync: c_int, ) -> c_uint; } } // The conventional solution is to use `mockall_double`. However, automock's requirement for an // inner module would require changing the imports in all the files using this module. This approach // allows all the other modules to keep using the `raw` module as before. #[cfg(not(test))] pub use ffi::*; #[cfg(not(test))] #[cfg(not(windows))] pub use ffi_unix::*; #[cfg(not(test))] #[cfg(target_os = "macos")] pub use ffi_macos::*; #[cfg(not(test))] #[cfg(windows)] pub use ffi_windows::*; #[cfg(test)] pub use mock_ffi::*; #[cfg(test)] #[cfg(not(windows))] pub use mock_ffi_unix::*; #[cfg(test)] #[cfg(target_os = "macos")] pub use mock_ffi_macos::*; #[cfg(test)] #[cfg(windows)] pub use mock_ffi_windows::*; #[cfg(test)] pub mod testmod { use std::{ffi::CString, sync::Mutex}; use once_cell::sync::Lazy; use super::*; pub struct GeterrContext(__pcap_geterr::Context); // Must be acquired by any test using mock FFI. pub static RAWMTX: Lazy> = Lazy::new(|| Mutex::new(())); pub fn as_pcap_t(value: &mut T) -> *mut pcap_t { value as *mut T as *mut pcap_t } pub fn as_pcap_dumper_t(value: &mut T) -> *mut pcap_dumper_t { value as *mut T as *mut pcap_dumper_t } pub fn geterr_expect(pcap: *mut pcap_t) -> GeterrContext { // Lock must be acquired by caller. assert!(RAWMTX.try_lock().is_err()); let err = CString::new("oh oh").unwrap(); let ctx = pcap_geterr_context(); ctx.checkpoint(); ctx.expect() .withf_st(move |arg1| *arg1 == pcap) .return_once_st(|_| err.into_raw()); GeterrContext(ctx) } } // GRCOV_EXCL_STOP pcap-2.2.0/src/sendqueue/mod.rs000064400000000000000000000001311046102023000144550ustar 00000000000000#[cfg(windows)] pub mod windows; #[cfg(windows)] pub use windows::{SendQueue, SendSync}; pcap-2.2.0/src/sendqueue/windows.rs000064400000000000000000000143611046102023000154020ustar 00000000000000//! WinPcap/npcap sendqueue support module. //! //! Sending individual packets through WinPcap/npcap can be stunningly slow, since a user-to-kernel //! transition is required for each packet transfer. To alleviate this there's support for //! queueing up a batch of packets in userland, requiring only a single transition to kernel to //! transmit them all. use std::convert::TryInto; use std::io::IoSlice; use std::ptr::NonNull; use crate::{ capture::{Active, Capture}, raw, Error, }; pub struct SendQueue(NonNull); pub enum SendSync { Off = 0, On = 1, } #[inline] fn make_pkthdr(ts: Option, len: u32) -> raw::pcap_pkthdr { raw::pcap_pkthdr { ts: if let Some(ts) = ts { libc::timeval { // tv_sec is currently i32 in libc when building for Windows tv_sec: ts.as_secs() as i32, tv_usec: ts.subsec_micros() as i32, } } else { libc::timeval { tv_sec: 0, tv_usec: 0, } }, caplen: len, len, } } impl SendQueue { /// Create a new `SendQueue` object with a maximum capacity of `memsize`. /// /// The buffer size `memsize` must be able to contain both packet headers and actual packet /// contents. pub fn new(memsize: u32) -> Result { let squeue = unsafe { raw::pcap_sendqueue_alloc(memsize) }; let squeue = NonNull::new(squeue).ok_or(Error::InsufficientMemory)?; Ok(Self(squeue)) } pub fn maxlen(&self) -> u32 { unsafe { self.0.as_ref().maxlen } } pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn len(&self) -> u32 { unsafe { self.0.as_ref().len } } /// Add a packet to the queue. /// /// The `ts` argument only needs to be a `Some()` value if the transmission mode will be /// synchronous when calling [`SendQueue::transmit()`]. pub fn queue(&mut self, ts: Option, buf: &[u8]) -> Result<(), Error> { let len = buf.len().try_into().ok().ok_or(Error::BufferOverflow)?; let pkthdr = make_pkthdr(ts, len); let ph = &pkthdr as *const _; let res = unsafe { raw::pcap_sendqueue_queue(self.0.as_ptr(), ph, buf.as_ptr()) }; if res == -1 { return Err(Error::InsufficientMemory); } Ok(()) } /// Add a (potentially) scattered packet to the queue. /// /// ``` /// use std::io::IoSlice; /// use pcap::sendqueue::SendQueue; /// let dstmac: [u8; 6] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; /// let srcmac: [u8; 6] = [0, 0, 0, 0, 0, 0]; /// let ethtype: [u8; 2] = [0x12, 0x34]; /// let payload: [u8; 5] = [0x00, 0x01, 0x02, 0x03, 0x04]; /// let iov = [ /// IoSlice::new(&dstmac), /// IoSlice::new(&srcmac), /// IoSlice::new(ðtype), /// IoSlice::new(&payload), /// ]; /// let mut sq = SendQueue::new(1024*1024).unwrap(); /// sq.queue_sg(None, &iov).unwrap(); /// ``` pub fn queue_sg( &mut self, ts: Option, iov: &[IoSlice<'_>], ) -> Result<(), Error> { // Calculate the total packet size from the scatter/gather list. let pktsize: usize = iov.iter().map(|b| b.len()).sum(); // Make sure there's enough room for packet header and (assembled) packet. // Note: It is assumed that len cannot exceed maxlen. This invariant must be upheld by // all methods implemented by SendQueue. let remain = (self.maxlen() - self.len()) as usize; let need = std::mem::size_of::() + pktsize; if remain < need { return Err(Error::BufferOverflow); } // SAFETY: // At this point it is know that the internal sendqueue buffer will fit the packet data, // and as such any further buffer length validations are not needed. let pktlen = pktsize.try_into().ok().ok_or(Error::BufferOverflow)?; // Generate a raw packet header and get a pointer to it. let pkthdr = make_pkthdr(ts, pktlen); let rawhdr = &pkthdr as *const _ as *const u8; // Get a raw pointer to the current write location in sendqueue's internal buffer. let rawsq = unsafe { self.0.as_mut() }; let sqbuf = rawsq.buffer as *mut u8; let bufoffs = rawsq.len.try_into().ok().ok_or(Error::BufferOverflow)?; let mut wbuf = unsafe { sqbuf.offset(bufoffs) }; // Copy packet header into the sendqueue's buffer let mut lastlen = std::mem::size_of::(); unsafe { std::ptr::copy_nonoverlapping(rawhdr, wbuf, lastlen); } // Iterate over scatter/gather list and copy each entry into the sendqueue's raw buffer for b in iov { // Get a write pointer at the next position let len = lastlen.try_into().ok().ok_or(Error::BufferOverflow)?; wbuf = unsafe { wbuf.offset(len) }; unsafe { std::ptr::copy_nonoverlapping(b.as_ptr(), wbuf, b.len()); } lastlen = b.len(); } // 'len' is used as write cursor rawsq.len += need as u32; Ok(()) } /// Transmit the contents of the queue. /// /// If entire queue was transmitted successfully the queue will be automatically reset. /// /// If `sync` is set to `SendSync::On` the difference between packet header timestamps /// will be used as a delay between sending each packet. If `SendSync::Off` is used the packets /// will be transmitted with no delay between packets. pub fn transmit(&mut self, dev: &mut Capture, sync: SendSync) -> Result<(), Error> { let res = unsafe { raw::pcap_sendqueue_transmit(dev.as_ptr(), self.0.as_ptr(), sync as i32) }; if res < self.len() { return unsafe { Err(Error::new(raw::pcap_geterr(dev.as_ptr()))) }; } else { self.reset(); } Ok(()) } pub fn reset(&mut self) { unsafe { self.0.as_mut() }.len = 0; } } impl Drop for SendQueue { fn drop(&mut self) { unsafe { raw::pcap_sendqueue_destroy(self.0.as_ptr()); } } } pcap-2.2.0/src/stream/mod.rs000064400000000000000000000024151046102023000137610ustar 00000000000000#[cfg(unix)] pub mod unix; #[cfg(unix)] pub use unix::PacketStream; #[cfg(windows)] pub mod windows; #[cfg(windows)] pub use windows::PacketStream; use crate::{ capture::{Activated, Capture}, codec::PacketCodec, Error, }; impl Capture { /// Returns this capture as a [`futures::Stream`] of packets. /// /// # Errors /// /// If this capture is set to be blocking, or if the network device /// does not support `select()`, an error will be returned. pub fn stream(self, codec: C) -> Result, Error> { if !self.is_nonblock() { return Err(Error::NonNonBlock); } PacketStream::new(self, codec) } } #[cfg(test)] mod tests { use crate::{ capture::{testmod::test_capture, Active}, codec::testmod::Codec, raw::testmod::{as_pcap_t, RAWMTX}, }; #[test] fn test_stream_error() { let _m = RAWMTX.lock(); let mut dummy: isize = 777; let pcap = as_pcap_t(&mut dummy); let test_capture = test_capture::(pcap); let capture = test_capture.capture; assert!(!capture.is_nonblock()); let result = capture.stream(Codec); assert!(result.is_err()); } } pcap-2.2.0/src/stream/unix.rs000064400000000000000000000041261046102023000141660ustar 00000000000000//! Support for asynchronous packet iteration. //! //! See [`Capture::stream`](super::Capture::stream). use std::io; use std::marker::Unpin; use std::pin::Pin; use std::task::{self, Poll}; use futures::ready; use tokio::io::unix::AsyncFd; use crate::{ capture::{selectable::SelectableCapture, Activated, Capture}, codec::PacketCodec, Error, }; /// Implement Stream for async use of pcap pub struct PacketStream { inner: AsyncFd>, codec: C, } impl PacketStream { pub(crate) fn new(capture: Capture, codec: C) -> Result { let capture = SelectableCapture::new(capture)?; Ok(PacketStream { inner: AsyncFd::with_interest(capture, tokio::io::Interest::READABLE)?, codec, }) } /// Returns a mutable reference to the inner [`Capture`]. /// /// The caller must ensure the capture will not be set to be blocking. pub fn capture_mut(&mut self) -> &mut Capture { self.inner.get_mut().get_inner_mut() } } impl Unpin for PacketStream {} impl futures::Stream for PacketStream { type Item = Result; fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { let stream = Pin::into_inner(self); let codec = &mut stream.codec; loop { let mut guard = ready!(stream.inner.poll_read_ready_mut(cx))?; match guard.try_io( |inner| match inner.get_mut().get_inner_mut().next_packet() { Ok(p) => Ok(Ok(codec.decode(p))), Err(e @ Error::TimeoutExpired) => { Err(io::Error::new(io::ErrorKind::WouldBlock, e)) } Err(e) => Ok(Err(e)), }, ) { Ok(result) => { return Poll::Ready(Some(result?)); } Err(_would_block) => continue, } } } } pcap-2.2.0/src/stream/windows.rs000064400000000000000000000074701046102023000147020ustar 00000000000000//! Support for asynchronous packet iteration. //! //! See [`Capture::stream`](super::Capture::stream). use std::marker::Unpin; use std::pin::Pin; use std::task::{self, Poll}; use futures::{ready, FutureExt}; use tokio::task::JoinHandle; use windows_sys::Win32::{Foundation::HANDLE, System::Threading::WaitForSingleObject}; use crate::{ capture::{Activated, Capture}, codec::PacketCodec, Error, }; /// Implement Stream for async use of pcap pub struct PacketStream { event_handle: EventHandle, capture: Capture, codec: C, } impl PacketStream { pub(crate) fn new(capture: Capture, codec: C) -> Result { Ok(Self { event_handle: EventHandle::new(&capture), capture, codec, }) } /// Returns a mutable reference to the inner [`Capture`]. /// /// The caller must ensure the capture will not be set to be blocking. pub fn capture_mut(&mut self) -> &mut Capture { &mut self.capture } } impl Unpin for PacketStream {} impl futures::Stream for PacketStream { type Item = Result; fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { let stream = Pin::into_inner(self); let codec = &mut stream.codec; loop { ready!(stream.event_handle.poll_ready(cx)); let res = match stream.capture.next_packet() { Ok(p) => Ok(codec.decode(p)), Err(Error::TimeoutExpired) => { stream.event_handle.clear_ready(); continue; } Err(e) => Err(e), }; return Poll::Ready(Some(res)); } } } /// A wrapper around a HANDLE that can be used to call `WaitForSingleObject` /// from an asynchronous context. Once the call to `WaitForSingleObject` /// completes, the handle is considered ready and will keep returning `Ready` /// until it's reset. struct EventHandle { handle: HANDLE, state: EventHandleState, } enum EventHandleState { /// We haven't started waiting for an event yet. Init, /// We're currently waiting for an event. Polling(JoinHandle<()>), /// We waited for an event. Ready, } impl EventHandle { pub fn new(capture: &Capture) -> Self { Self { handle: unsafe { // SAFETY: PacketStream stores the handle before the capture, // so the handle will be dropped before the capture. capture.get_event() }, state: EventHandleState::Init, } } pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<()> { loop { match self.state { EventHandleState::Init => { let handle = self.handle; self.state = EventHandleState::Polling(tokio::task::spawn_blocking(move || { const INFINITE: u32 = !0; unsafe { WaitForSingleObject(handle, INFINITE); } })); } EventHandleState::Polling(ref mut join_handle) => { let _ = ready!(join_handle.poll_unpin(cx)); self.state = EventHandleState::Ready; } EventHandleState::Ready => return Poll::Ready(()), } } } /// Reset the internal state. This will trigger a call to /// `WaitForSingleObject` the next time `poll_ready` is called. pub fn clear_ready(&mut self) { self.state = EventHandleState::Init; } } pcap-2.2.0/tests/capture/activated/mod.rs000064400000000000000000000106271046102023000164540ustar 00000000000000mod offline; use tempfile::TempDir; use pcap::{Capture, Linktype}; use crate::{capture_from_test_file, Packets}; #[test] fn read_packet_with_full_data() { let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); assert_eq!(capture.next_packet().unwrap().len(), 98); } #[test] fn read_packet_with_truncated_data() { let mut capture = capture_from_test_file("packet_snaplen_20.pcap"); assert_eq!(capture.next_packet().unwrap().len(), 20); } #[test] fn capture_dead_savefile() { let mut packets = Packets::new(); packets.push(1460408319, 1234, 1, 1, &[1]); packets.push(1460408320, 4321, 1, 1, &[2]); let dir = TempDir::new().unwrap(); let tmpfile = dir.path().join("test.pcap"); let cap = Capture::dead(Linktype(1)).unwrap(); let mut save = cap.savefile(&tmpfile).unwrap(); packets.foreach(|p| save.write(p)); drop(save); let mut cap = Capture::from_file(&tmpfile).unwrap(); packets.verify(&mut cap); } #[test] #[cfg(libpcap_1_7_2)] fn capture_dead_savefile_append() { let mut packets1 = Packets::new(); packets1.push(1460408319, 1234, 1, 1, &[1]); packets1.push(1460408320, 4321, 1, 1, &[2]); let mut packets2 = Packets::new(); packets2.push(1460408321, 2345, 1, 1, &[3]); packets2.push(1460408322, 5432, 1, 1, &[4]); let packets = &packets1 + &packets2; let dir = TempDir::new().unwrap(); let tmpfile = dir.path().join("test.pcap"); let cap = Capture::dead(Linktype(1)).unwrap(); let mut save = cap.savefile(&tmpfile).unwrap(); packets1.foreach(|p| save.write(p)); drop(save); let cap = Capture::dead(Linktype(1)).unwrap(); let mut save = cap.savefile_append(&tmpfile).unwrap(); packets2.foreach(|p| save.write(p)); drop(save); let mut cap = Capture::from_file(&tmpfile).unwrap(); packets.verify(&mut cap); } #[test] fn test_linktype() { let capture = capture_from_test_file("packet_snaplen_65535.pcap"); let linktype = capture.get_datalink(); assert!(linktype.get_name().is_ok()); assert_eq!(linktype.get_name().unwrap(), String::from("EN10MB")); assert!(linktype.get_description().is_ok()); } #[test] fn test_error() { let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); // Trying to get stats from offline capture should error. assert!(capture.stats().err().is_some()); } #[test] fn test_compile() { let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); let packet = capture.next_packet().unwrap(); let bpf_capture = Capture::dead(Linktype::ETHERNET).unwrap(); let program = bpf_capture.compile("dst host 8.8.8.8", false).unwrap(); let instructions = program.get_instructions(); assert!(!instructions.is_empty()); assert!(program.filter(packet.data)); let program = bpf_capture.compile("src host 8.8.8.8", false).unwrap(); let instructions = program.get_instructions(); assert!(!instructions.is_empty()); assert!(!program.filter(packet.data)); } #[test] fn test_compile_optimized() { let bpf_capture = Capture::dead(Linktype::ETHERNET).unwrap(); let program_str = "ip and ip and tcp"; let program_unopt = bpf_capture.compile(program_str, false).unwrap(); let instr_unopt = program_unopt.get_instructions(); let program_opt = bpf_capture.compile(program_str, true).unwrap(); let instr_opt = program_opt.get_instructions(); assert!(instr_opt.len() < instr_unopt.len()); } #[test] fn test_compile_error() { let bpf_capture = Capture::dead(Linktype::ETHERNET).unwrap(); let program_str = "this is a terrible program"; let result = bpf_capture.compile(program_str, false); assert!(result.is_err()); let result = bpf_capture.compile(program_str, true); assert!(result.is_err()); } #[test] fn test_filter() { let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); capture.filter("dst host 8.8.8.8", false).unwrap(); let result = capture.next_packet(); assert!(result.is_ok()); } #[test] fn read_packet_via_pcap_loop() { let mut packets = 0; let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); capture .for_each(None, |_| { packets += 1; }) .unwrap(); assert_eq!(packets, 1); } #[test] #[should_panic] fn panic_in_pcap_loop() { let mut capture = capture_from_test_file("packet_snaplen_65535.pcap"); capture.for_each(None, |_| panic!()).unwrap(); } pcap-2.2.0/tests/capture/activated/offline.rs000064400000000000000000000004221046102023000173070ustar 00000000000000use crate::capture_from_test_file; #[test] fn test_pcap_version() { let capture = capture_from_test_file("packet_snaplen_65535.pcap"); assert_eq!(capture.version(), (2, 4)); assert_eq!(capture.major_version(), 2); assert_eq!(capture.minor_version(), 4); } pcap-2.2.0/tests/capture/mod.rs000064400000000000000000000000171046102023000145000ustar 00000000000000mod activated; pcap-2.2.0/tests/data/packet_snaplen_20.pcap000064400000000000000000000000741046102023000167610ustar 00000000000000ò}֧Q3Rb;`3KXET&pcap-2.2.0/tests/data/packet_snaplen_65535.pcap000064400000000000000000000002121046102023000172210ustar 00000000000000ò}֧Q3Rbb;`3KXET&@W+ ;Q}Q  !"#$%&'()*+,-./01234567pcap-2.2.0/tests/lib.rs000064400000000000000000000141741046102023000130350ustar 00000000000000mod capture; use std::ops::Add; use std::path::Path; use pcap::{Activated, Capture, Offline, Packet, PacketHeader}; #[cfg(not(windows))] #[allow(non_camel_case_types)] type time_t = libc::time_t; #[cfg(windows)] #[allow(non_camel_case_types)] type time_t = libc::c_long; #[cfg(not(windows))] #[allow(non_camel_case_types)] type suseconds_t = libc::suseconds_t; #[cfg(windows)] #[allow(non_camel_case_types)] type suseconds_t = libc::c_long; fn capture_from_test_file(file_name: &str) -> Capture { let path = Path::new("tests/data/").join(file_name); Capture::from_file(path).unwrap() } #[derive(Clone)] pub struct Packets { headers: Vec, data: Vec>, } impl Default for Packets { fn default() -> Self { Self::new() } } impl Packets { pub fn new() -> Packets { Packets { headers: vec![], data: vec![], } } pub fn push( &mut self, tv_sec: time_t, tv_usec: suseconds_t, caplen: u32, len: u32, data: &[u8], ) { self.headers.push(PacketHeader { ts: libc::timeval { tv_sec, tv_usec }, caplen, len, }); self.data.push(data.to_vec()); } pub fn foreach(&self, mut f: F) { for (header, data) in self.headers.iter().zip(self.data.iter()) { let packet = Packet::new(header, data); f(&packet); } } pub fn verify(&self, cap: &mut Capture) { for (header, data) in self.headers.iter().zip(self.data.iter()) { assert_eq!(cap.next_packet().unwrap(), Packet::new(header, data)); } assert!(cap.next_packet().is_err()); } } impl<'a> Add for &'a Packets { type Output = Packets; fn add(self, rhs: &'a Packets) -> Packets { let mut packets = self.clone(); packets.headers.extend(rhs.headers.iter()); packets.data.extend(rhs.data.iter().cloned()); packets } } #[test] #[cfg(not(windows))] fn test_raw_fd_api() { use std::fs::File; use std::io; use std::io::prelude::*; use std::os::unix::io::{FromRawFd, RawFd}; use std::thread; use tempfile::TempDir; use pcap::Linktype; use pcap::{Error, Precision}; // Create a total of more than 64K data (> max pipe buf size) const N_PACKETS: usize = 64; let data: Vec = (0..191).cycle().take(N_PACKETS * 1024).collect(); let mut packets = Packets::new(); for i in 0..N_PACKETS { packets.push( 1460408319 + i as time_t, 1000 + i as suseconds_t, 1024, 1024, &data[i * 1024..(i + 1) * 1024], ); } let dir = TempDir::new().unwrap(); let tmpfile = dir.path().join("test.pcap"); // Write all packets to test.pcap savefile let cap = Capture::dead(Linktype(1)).unwrap(); let mut save = cap.savefile(&tmpfile).unwrap(); packets.foreach(|p| save.write(p)); drop(save); assert_eq!( unsafe { Capture::from_raw_fd(-999) }.err().unwrap(), Error::InvalidRawFd ); #[cfg(libpcap_1_5_0)] { assert_eq!( unsafe { Capture::from_raw_fd_with_precision(-999, Precision::Micro) } .err() .unwrap(), Error::InvalidRawFd ); } assert_eq!( unsafe { cap.savefile_raw_fd(-999) }.err().unwrap(), Error::InvalidRawFd ); // Create an unnamed pipe let mut pipe = [0 as libc::c_int; 2]; assert_eq!(unsafe { libc::pipe(pipe.as_mut_ptr()) }, 0); let (fd_in, fd_out) = (pipe[0], pipe[1]); let filename = dir.path().join("test2.pcap"); let packets_c = packets.clone(); let pipe_thread = thread::spawn(move || { // Write all packets to the pipe let cap = Capture::dead(Linktype(1)).unwrap(); let mut save = unsafe { cap.savefile_raw_fd(fd_out) }.unwrap(); packets_c.foreach(|p| save.write(p)); // fd_out will be closed by savefile destructor }); // Save the pcap from pipe in a separate thread. // Hypothetically, we could do any sort of processing here, // like encoding to a gzip stream. let mut file_in = unsafe { File::from_raw_fd(fd_in) }; let mut file_out = File::create(filename).unwrap(); io::copy(&mut file_in, &mut file_out).unwrap(); // Verify that the contents match let filename = dir.path().join("test2.pcap"); let (mut v1, mut v2) = (vec![], vec![]); File::open(&tmpfile).unwrap().read_to_end(&mut v1).unwrap(); File::open(filename).unwrap().read_to_end(&mut v2).unwrap(); assert_eq!(v1, v2); // Join thread. pipe_thread.join().unwrap(); #[cfg(libpcap_1_5_0)] unsafe fn from_raw_fd_with_precision(fd: RawFd, precision: Precision) -> Capture { Capture::from_raw_fd_with_precision(fd, precision).unwrap() } #[cfg(not(libpcap_1_5_0))] unsafe fn from_raw_fd_with_precision(fd: RawFd, _: Precision) -> Capture { Capture::from_raw_fd(fd).unwrap() } for with_tstamp in &[false, true] { // Create an unnamed pipe let mut pipe = [0 as libc::c_int; 2]; assert_eq!(unsafe { libc::pipe(pipe.as_mut_ptr()) }, 0); let (fd_in, fd_out) = (pipe[0], pipe[1]); let filename = tmpfile.clone(); let pipe_thread = thread::spawn(move || { // Cat the pcap into the pipe in a separate thread. // Hypothetically, we could do any sort of processing here, // like decoding from a gzip stream. let mut file_in = File::open(&filename).unwrap(); let mut file_out = unsafe { File::from_raw_fd(fd_out) }; io::copy(&mut file_in, &mut file_out).unwrap(); }); // Open the capture with pipe's file descriptor let mut cap = if *with_tstamp { unsafe { from_raw_fd_with_precision(fd_in, Precision::Micro) } } else { unsafe { Capture::from_raw_fd(fd_in) }.unwrap() }; // Verify that packets match packets.verify(&mut cap); // Join thread. pipe_thread.join().unwrap(); } } pcap-2.2.0/tests/tap_tests.rs000064400000000000000000000141051046102023000142670ustar 00000000000000/*** * These tests need to be run as root and currently only work on Linux (and maybe MacOs?) * * To build and run these tests, run: * cargo test --no-run --test tap_tests |& \ sed -e 's/[()]//g' | \ awk '/Executable/ {print $3" --include-ignored"}' | \ xargs sudo * which does the build as a non-priv user, extracts the exec binary location of * the test from 'cargo test', and runs only that as root. * * To develop on these tests, you need to manually enable this feature in VSCode ala: * * In VS Code, open the Extensions sidebar, click the gear icon next * to the rust-analyzer extension, and choose “Extension Settings.” * You can choose whether to customize settings for all projects (the * “User” tab) or just the current one (the “Workspace” tab). The * setting is labeled “Cargo: Features.” * * [from https://users.rust-lang.org/t/passing-feature-flags-to-rust-analyzer/45918/3] * * to debug, run: * * sudo rust-gdb ./target/debug/deps/tap_test-${BUILD} * break tap_test::tests:: # to get a list of useful breakpoints * * NOTE: tests in rust capture stdio/stderr by default; add "-- --nocapture", e.g., * 'cargo test -- --nocapture' */ #[cfg(not(windows))] mod tests { use etherparse::{PacketBuilder, PacketHeaders}; use pcap::Capture; use tun_tap::Iface; /*** * Create a Tap interface and make sure that the sendpacket() and next_packet() * work as expected */ #[test] #[ignore] fn conntrack_tap_basic() { let (cap, iface) = capture_tap_interface(); // NOTE: on Linux, if you don't specify a timeout(), it will never return let mut cap = cap.snaplen(32000).timeout(500).open().unwrap(); // create a test packet let builder1 = PacketBuilder::ethernet2([1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2]) .ipv4([1, 2, 3, 4], [5, 6, 7, 8], 128) .tcp(80, 12345, 1, 32000); let payload1 = [1, 2, 3, 4, 5, 6, 7, 8]; let mut pkt1 = Vec::with_capacity(builder1.size(payload1.len())); builder1.write(&mut pkt1, &payload1).unwrap(); // send it in the interface let send_len = iface.send(&pkt1).unwrap(); assert_eq!(send_len, pkt1.len()); // try to pcap capture it let test_pkt1 = cap.next_packet().unwrap(); // did we capture the whole packet? assert_eq!(pkt1.len(), test_pkt1.header.caplen as usize); // does it match what we expect? assert_eq!(&pkt1, test_pkt1.data); // now, try to pcap send it back out that interface cap.sendpacket(pkt1.clone()).unwrap(); let mut buf = vec![0; pkt1.len() * 2]; let recv_len = iface.recv(&mut buf).unwrap(); let (test_sendpkt, _) = buf.split_at(recv_len); if recv_len != pkt1.len() { // wtf!? let weird = PacketHeaders::from_ethernet_slice(test_sendpkt).unwrap(); panic!("weird packet !! {:#?}", weird); } assert_eq!(pkt1.len(), recv_len); assert_eq!(pkt1, test_sendpkt); } /** * Bind a tap interface and attach a pcap capture to it and return both * * Return as a Capture in case the caller wants to set some * different options before opening it (maybe?) */ fn capture_tap_interface() -> (Capture, Iface) { use tun_tap::Mode; // without_packet_info sets ioctl(fd, IFF_NO_PI ) on the tap fd // as described in https://www.gabriel.urdhr.fr/2021/05/08/tuntap/#packet-information // it's not useful for l2 tap packets, so wouldl like to skip to simplify tests let iface_result = Iface::without_packet_info("testtap%d", Mode::Tap); // I know this could/should be a match(), but I think this is cleaner... if let Err(e) = iface_result { if e.kind() == std::io::ErrorKind::PermissionDenied { println!("Permission denied - needs tp be run as root/sudo!"); panic!("Failed to bind the tap interface: PermissionDenied - please run with root/sudo!"); } // common error is to not run these tests as root; provide a nicer message panic!("Failed to bind the tap interface: {:#?}", e); } let iface = iface_result.unwrap(); if cfg!(target_os = "linux") { // If IPv6 is enabled, it will broadcast all sorts of stuff on this interface // these broadcasts will periodically (heisenbug!) break tests that aren't smart enough // so disable IPv6 on the test interface before we start any captures // It's important to do this BEFORE bringing up the interface else there's still // a race condition (that we were losing more often than not!) safe_run_command(format!( "sysctl -w net.ipv6.conf.{}.disable_ipv6=1", iface.name() )); // Under Linux, the interface is created, but defaults to 'down' state, where pcap needs it 'up' // Yes, it's a hack to use the command line instead of an API, but the netdev APIs are messy // TODO: decide if we should move to the https://crates.io/keywords/netlink crate safe_run_command(format!("ip link set dev {} up", iface.name())); } let device = pcap::Device::from(iface.name()); (Capture::from_device(device).unwrap(), iface) } /** * Run a command on the shell, check the output, and pretty print a panic message and the * stderr if it fails. */ fn safe_run_command(cmd: String) { use std::process::Command; let mut split_cmd = cmd.split_ascii_whitespace(); // the first token is the program and the rest are args() let output = Command::new(split_cmd.next().unwrap()) .args(split_cmd.collect::>()) .output() .unwrap(); if !output.status.success() { panic!( "safe_run_command FAILED: '{}' command returned stderr '{:#?}'", cmd, String::from_utf8(output.stderr) ); } } }