diff --git a/.cirrus.yml b/.cirrus.yml index 56788c6520..dd54cc81e4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,7 +6,7 @@ task: name: Test FreeBSD freebsd_instance: matrix: - image_family: freebsd-13-2 + image_family: freebsd-13-3 image_family: freebsd-14-0 install_script: - pkg install -y bash cmake git gmake gsed libpcap tcpreplay diff --git a/.github/ISSUE_TEMPLATE/blank.md b/.github/ISSUE_TEMPLATE/blank.md new file mode 100644 index 0000000000..88689a4175 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank.md @@ -0,0 +1,6 @@ +--- +name: Blank issue +about: Report a issue with no specific category +--- + +(A clear and concise description of the issue.) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000000..20a8822f27 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,103 @@ +name: Bug report +description: Submit a bug report +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + **New to PcapPlusPlus?** + + For help or advice on using PcapPlusPlus, try one of the following options instead of opening a GitHub issue: + + - Post a message in PcapPlusPlus Google group: https://groups.google.com/d/forum/pcapplusplus-support + - Ask a question on Stack Overflow: https://stackoverflow.com/questions/tagged/pcapplusplus + - Send an email to: pcapplusplus@gmail.com + - Follow us on Twitter: https://twitter.com/seladb + + Make sure to also search the [PcapPlusPlus issue tracker](https://github.com/seladb/PcapPlusPlus/issues) to check that the bug has not already been reported. + + You can check more information about how to report a bug in the [PcapPlusPlus page](https://pcapplusplus.github.io/community#report-an-issue). + - type: textarea + attributes: + label: "Bug description" + description: > + Give a clear and concise description of what happened. + Include a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) if possible. + [Copy and paste code where possible rather than using screenshots](https://meta.stackoverflow.com/a/285557/13990016), + and put any code blocks inside triple backticks. + + value: | + **Describe the bug** + A clear and concise description of what the bug is. + + **Code example to reproduce** + ```cpp + // Add a code block here, if required + ``` + + **Expected behavior** + A clear and concise description of what you expected to happen. + validations: + required: true + - type: dropdown + attributes: + label: "PcapPlusPlus versions tested on" + multiple: true + options: + - "PcapPlusPlus master branch" + - "v23.09" + - "v22.11" + - "v22.05" + - "v21.11" + - "v21.05" + - "Other (specify in the next question)" + validations: + required: true + - type: input + attributes: + label: "Other PcapPlusPlus version (if applicable)" + description: If you select "Other", please specify the version of PcapPlusPlus you tested on. + placeholder: ex. Build with commit hash 1234567 + validations: + required: false + - type: dropdown + attributes: + label: "Operating systems tested on" + multiple: true + options: + - Linux + - macOS + - Windows + - FreeBSD + - Android + - Other (specify in the next question) + validations: + required: true + - type: input + attributes: + label: "Other operation systems (if applicable)" + description: If you select "Other", please specify the operation system you tested on. + placeholder: ex. ChromeOS, Solaris + validations: + required: false + - type: input + attributes: + label: "Compiler version" + description: Please specify the compiler version you used. + placeholder: ex. GCC 10.2.0 + validations: + required: true + - type: dropdown + attributes: + label: "Packet capture backend (if applicable)" + multiple: true + options: + - libpcap + - PF_RING + - DPDK + - WinPcap + - Npcap + - XDP + - N/A + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..a281debaba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: "Contact the PcapPlusPlus team" + about: "For help or advice on using PcapPlusPlus, try one of the following options instead of opening a GitHub issue:" + url: "https://pcapplusplus.github.io/community" + - name: "Documentation" + about: "Check the PcapPlusPlus documentation for more information." + url: "https://pcapplusplus.github.io/ " diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000000..1baa2487c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,9 @@ +--- +name: Documentation +about: Report a problem with the documentation +labels: "documentation" +--- + +# Documentation + +(A clear and concise description of the issue.) diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 0000000000..82539d7988 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,40 @@ +name: Question +description: Submit a question +labels: ["question"] +body: + - type: markdown + attributes: + value: | + # Asking a question + If you have a question about PcapPlusPlus, please check the following resources before opening a new issue: + - [PcapPlusPlus documentation](https://pcapplusplus.github.io/docs/quickstart) + - [PcapPlusPlus API reference](https://pcapplusplus.github.io/docs/api) + - [GitHub issues tracker](https://github.com/seladb/PcapPlusPlus/issues) + + - type: textarea + attributes: + label: "Question" + description: > + Explain your question in detail. + value: | + ```cpp + # Add a code block here, if required + ``` + validations: + required: true + + - type: dropdown + attributes: + label: "Operating systems" + description: > + Select the operating systems that you are interested in. + multiple: true + options: + - Linux + - macOS + - Windows + - FreeBSD + - Android + - Other (specify in the question) + validations: + required: false diff --git a/.github/workflows/auto_update.yml b/.github/workflows/auto_update.yml index ca6dd18984..8582a269bd 100644 --- a/.github/workflows/auto_update.yml +++ b/.github/workflows/auto_update.yml @@ -2,7 +2,10 @@ name: Auto Update on: schedule: - - cron: '10 10 15 * *' + - cron: '0 0 1 * *' # Runs at 00:00, on day 1 of the month + +permissions: + contents: read jobs: precommit-update: @@ -10,11 +13,11 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: ref: dev - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.8.x" - name: Run update @@ -22,7 +25,7 @@ jobs: pip install pre-commit pre-commit autoupdate - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: token: ${{ secrets.PAT }} author: GitHub @@ -43,11 +46,11 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: ref: dev - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.9.x" - name: Run update @@ -55,7 +58,7 @@ jobs: python3 3rdParty/OUIDataset/create_oui_data.py mv -f PCPP_OUIDataset.json 3rdParty/OUIDataset/PCPP_OUIDataset.json - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: token: ${{ secrets.PAT }} author: GitHub diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 7e08dabb31..ccfd2ce65a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,18 +4,23 @@ on: branches: ["master", "dev"] pull_request: branches: ["dev"] + schedule: + - cron: '0 0 * * 0' # Run every Sunday at midnight env: BUILD_DIR: Dist GCOVR_FLAGS: --gcov-ignore-parse-errors --exclude-throw-branches --filter Common --filter Pcap --filter Packet --xml +permissions: + contents: read + jobs: pre-commit: runs-on: ubuntu-latest container: seladb/alpine317 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Checkout is performed out of the container and doesn't match our user - name: Fix checkout ownership @@ -26,7 +31,7 @@ jobs: apk update && apk add cppcheck python3-dev python3 -m pip install cmake-format - - uses: pre-commit/action@v3.0.1 + - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 - name: CMake format run: | @@ -73,7 +78,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Checkout is performed out of the container and doesn't match our user - name: Fix checkout ownership @@ -124,12 +129,14 @@ jobs: gcovr -v -r . ${{ matrix.additional-gcov-flags }} $GCOVR_FLAGS -o coverage.xml - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./coverage.xml flags: ${{ matrix.image }},unittest fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} dpdk: runs-on: ubuntu-latest @@ -145,7 +152,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Configure PcapPlusPlus run: cmake -DPCAPPP_USE_DPDK=ON ${{ matrix.additional-flags }} -S . -B "$BUILD_DIR" @@ -190,7 +197,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Configure PcapPlusPlus run: ${{ matrix.configure }} @@ -238,9 +245,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: # support version: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json python-version: "3.12" @@ -282,15 +289,15 @@ jobs: cmake --build "$BUILD_DIR" -j cmake --install $BUILD_DIR - # - name: Build Tutorials - # run: | - # mkdir -p build_examples - # cmake -DPCAPPP_BUILD_TUTORIALS=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -S Examples -B build_examples - # cmake --build build_examples -j + - name: Build Tutorials + run: | + mkdir -p build_examples + cmake -DPCAPPP_BUILD_TUTORIALS=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -S Examples -B build_examples + cmake --build build_examples -j - # - name: Test Tutorials - # if: ${{ matrix.arch == 'x86_64' }} - # run: cd build_examples/tutorials_bin && ./Tutorial-HelloWorld + - name: Test Tutorials + if: ${{ matrix.arch == 'x86_64' }} + run: cd build_examples/tutorials_bin && ./Tutorial-HelloWorld - name: Create Cobertura Report run: | @@ -298,12 +305,14 @@ jobs: gcovr -v -r . $GCOVR_FLAGS -o coverage.xml - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./coverage.xml flags: ${{ matrix.os-version }},unittest fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} macos-m1: runs-on: ${{ matrix.os-version }} @@ -313,9 +322,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: # support version: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json python-version: "3.12" @@ -356,14 +365,14 @@ jobs: cmake --build "$BUILD_DIR" -j cmake --install "$BUILD_DIR" - # - name: Build Tutorials - # run: | - # mkdir -p build_examples - # cmake -DPCAPPP_BUILD_TUTORIALS=ON -S Examples -B build_examples - # cmake --build build_examples -j + - name: Build Tutorials + run: | + mkdir -p build_examples + cmake -DPCAPPP_BUILD_TUTORIALS=ON -S Examples -B build_examples + cmake --build build_examples -j - # - name: Test Tutorials - # run: cd build_examples/tutorials_bin && ./Tutorial-HelloWorld + - name: Test Tutorials + run: cd build_examples/tutorials_bin && ./Tutorial-HelloWorld - name: Create Cobertura Report run: | @@ -371,12 +380,14 @@ jobs: gcovr -v -r . $GCOVR_FLAGS -o coverage.xml - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./coverage.xml flags: ${{ matrix.os-version }},unittest fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} mingw-w64: runs-on: windows-latest @@ -390,10 +401,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 + uses: msys2/setup-msys2@d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8 # v2.23.0 with: msystem: ${{matrix.sys}} install: >- @@ -403,7 +414,7 @@ jobs: mingw-w64-${{matrix.env}}-make - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.8.x" @@ -451,12 +462,14 @@ jobs: run: gcovr -v -g -k -r . $env:GCOVR_FLAGS.split() -o coverage.xml - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./coverage.xml flags: ${{ matrix.sys }},unittest fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} visual-studio: strategy: @@ -482,14 +495,14 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.8.x" - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 - name: Setup OpenCppCoverage and add to PATH run: | @@ -539,12 +552,14 @@ jobs: python -m pytest --root-path=../../Dist/examples_bin - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./Tests/Pcap++Test/Pcap++Coverage.xml,./Tests/Packet++Test/Packet++Coverage.xml flags: ${{ matrix.os }},unittest,${{ matrix.pcap_lib }} fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} android: strategy: @@ -569,10 +584,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Checkout lipbcap for Android - uses: actions/checkout@main + uses: actions/checkout@cd7d8d697e10461458bc61a30d094dc601a8b017 # main with: repository: seladb/libpcap-android path: ./libpcap-android @@ -586,7 +601,7 @@ jobs: run: cmake --build "$BUILD_DIR" -j - name: Checkout ToyVpn-PcapPlusPlus - uses: actions/checkout@master + uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 # master with: repository: seladb/ToyVpn-PcapPlusPlus path: ./ToyVpn-PcapPlusPlus @@ -617,7 +632,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Install dependencies run: | @@ -641,12 +656,14 @@ jobs: gcovr -v -r . $GCOVR_FLAGS -o coverage.xml - name: Upload Coverage Results - uses: codecov/codecov-action@v3.1.6 + uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 with: files: ./coverage.xml flags: xdp,unittest fail_ci_if_error: false verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} test_regressions: name: Run ${{ matrix.engine }}-${{ matrix.sanitizer }} fuzzer for regressions @@ -659,7 +676,7 @@ jobs: container: image: gcr.io/oss-fuzz-base/base-builder steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install prerequisites run: | apt-get update && apt-get install -y cmake autoconf flex bison diff --git a/.github/workflows/check_dependabot.yml b/.github/workflows/check_dependabot.yml index a7bbbfcd8e..5b46b6c383 100644 --- a/.github/workflows/check_dependabot.yml +++ b/.github/workflows/check_dependabot.yml @@ -6,10 +6,13 @@ on: - '.github/dependabot.yml' - '.github/workflows/check_dependabot.yml' +permissions: + contents: read + jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: marocchino/validate-dependabot@v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: marocchino/validate-dependabot@d8ae5c0d03dd75fbd0ad5f8ab4ba8101ebbd4b37 # v3.0.0 id: validate diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 899d5768a5..84805e64a6 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -2,6 +2,11 @@ name: Fuzz CI on: pull_request: branches: ["dev"] + schedule: + - cron: '0 0 * * 0' # Run every Sunday at midnight + +permissions: + contents: read jobs: Fuzzing: @@ -13,20 +18,20 @@ jobs: steps: - name: Build Fuzzers id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@c2c0632831767ff05c568e7b552cef2801d739ff # master with: oss-fuzz-project-name: 'pcapplusplus' dry-run: false sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@c2c0632831767ff05c568e7b552cef2801d739ff # master with: oss-fuzz-project-name: 'pcapplusplus' fuzz-seconds: 600 dry-run: false sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 95f692b9b1..bf6b0dc7db 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,10 +2,13 @@ name: "CodeQL" on: push: - branches: [ "dev" ] + branches: [ "master", "dev" ] workflow_dispatch: schedule: - - cron: '15 14 * * 1' + - cron: '0 0 * * 0' # Run every Sunday at midnight + +permissions: + contents: read jobs: analyze: @@ -22,11 +25,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -41,4 +44,4 @@ jobs: cmake --build build -j - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 1b1a5b762f..c97d658999 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -8,13 +8,20 @@ on: paths: # Also run this workflow when this package.yml is update by a PR - '.github/workflows/package.yml' + schedule: + - cron: '0 0 * * 0' # Run every Sunday at midnight env: BUILD_DIR: Dist +permissions: + contents: read + jobs: linux: runs-on: ubuntu-latest + permissions: + contents: write container: seladb/${{ matrix.image }} strategy: matrix: @@ -37,7 +44,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Checkout is performed out of the container and doesn't match our user - name: Fix checkout ownership @@ -63,7 +70,7 @@ jobs: - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true @@ -72,6 +79,8 @@ jobs: freebsd: runs-on: ubuntu-latest + permissions: + contents: write strategy: matrix: include: @@ -79,10 +88,10 @@ jobs: - freebsd-version: "14.0" steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Test in FreeBSD - uses: vmactions/freebsd-vm@v1 + uses: vmactions/freebsd-vm@f8be330398166d1eb0601f01353839d4052367b2 # v1.0.7 with: release: ${{ matrix.freebsd-version }} envs: 'BUILD_DIR' @@ -96,7 +105,7 @@ jobs: - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true @@ -105,18 +114,20 @@ jobs: macos: runs-on: macos-12 + permissions: + contents: write strategy: matrix: xcode-version: [14.2.0, 13.4.1] arch: [x86_64, arm64] steps: - - uses: maxim-lobanov/setup-xcode@v1 + - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 with: xcode-version: "${{ matrix.xcode-version }}" - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Configure PcapPlusPlus run: | @@ -130,7 +141,7 @@ jobs: - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true @@ -139,6 +150,8 @@ jobs: mingw-w64: runs-on: windows-latest + permissions: + contents: write strategy: matrix: include: @@ -149,10 +162,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 + uses: msys2/setup-msys2@d0e80f58dffbc64f6a3a1f43527d469b4fc7b6c8 # v2.23.0 with: msystem: ${{matrix.sys}} update: true @@ -191,7 +204,7 @@ jobs: - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true @@ -206,12 +219,14 @@ jobs: configuration: [ Debug, Release ] runs-on: ${{ matrix.os }} + permissions: + contents: write steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 - name: Install WinPcap run: | @@ -231,7 +246,7 @@ jobs: - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true @@ -255,10 +270,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Checkout lipbcap for Android - uses: actions/checkout@main + uses: actions/checkout@cd7d8d697e10461458bc61a30d094dc601a8b017 # main with: repository: seladb/libpcap-android path: ./libpcap-android @@ -288,7 +303,7 @@ jobs: mkdir -p "android-package" mv "${COMBINED_PACKAGE_DIR}" "android-package" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: path: android-package name: android-package-${{ matrix.target }}-${{ matrix.api-version }} @@ -297,9 +312,11 @@ jobs: android-package: needs: android-build runs-on: ubuntu-latest + permissions: + contents: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 with: pattern: android-package-* merge-multiple: true @@ -311,7 +328,7 @@ jobs: tar cvf "${PACKAGE_DIR}.tar.gz" "${PACKAGE_DIR}" - name: Upload binaries to release if: github.ref_type == 'tag' - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: draft: true allowUpdates: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 0000000000..da39647d47 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,71 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["master"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + repo_token: ${{ secrets.PAT }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index f5c209626c..463ce5df94 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ Tests/Pcap++Test/cUrl/curl_output.txt *.VC.db *.vcxproj.user **/.vs +CMakeSettings.json #Visual Studio Projects mk/vs2015/** diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6970527fb3..f80c940238 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: mixed-line-ending args: ['--fix=lf'] - repo: https://github.com/psf/black - rev: 24.4.0 + rev: 24.4.2 hooks: - id: black - repo: https://github.com/pocc/pre-commit-hooks @@ -20,12 +20,12 @@ repos: args: ["--std=c++11", "--language=c++", "--suppressions-list=cppcheckSuppressions.txt", "--inline-suppr", "--force"] # - id: clang-format - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell pass_filenames: false - repo: https://github.com/crate-ci/typos - rev: v1.20.8 + rev: v1.21.0 hooks: - id: typos args: ['--config=typos-config.toml'] diff --git a/3rdParty/LightPcapNg/CMakeLists.txt b/3rdParty/LightPcapNg/CMakeLists.txt index 2f10732f09..cc79a42b33 100644 --- a/3rdParty/LightPcapNg/CMakeLists.txt +++ b/3rdParty/LightPcapNg/CMakeLists.txt @@ -24,6 +24,13 @@ add_library( target_compile_definitions(light_pcapng PUBLIC -DUNIVERSAL) +# FIXME: https://github.com/seladb/PcapPlusPlus/issues/1347 +include(CheckCCompilerFlag) +check_c_compiler_flag(-Wincompatible-pointer-types HAVE_W_INCOMPATIBLE_POINTER_TYPES) +if(HAVE_W_INCOMPATIBLE_POINTER_TYPES) + target_compile_options(light_pcapng PRIVATE -Wno-incompatible-pointer-types) +endif() + if(BUILD_SHARED_LIBS) set_property(TARGET light_pcapng PROPERTY POSITION_INDEPENDENT_CODE ON) endif() diff --git a/3rdParty/OUIDataset/PCPP_OUIDataset.json b/3rdParty/OUIDataset/PCPP_OUIDataset.json index f3034bbf8f..17aefde6c4 100644 --- a/3rdParty/OUIDataset/PCPP_OUIDataset.json +++ b/3rdParty/OUIDataset/PCPP_OUIDataset.json @@ -1977,7 +1977,7 @@ "vendor": "Tokyo Sokushin Co., Ltd." }, "661": { - "vendor": "IP.Access Limited" + "vendor": "Mavenir Ipa Uk Ltd" }, "662": { "vendor": "Lectron Co,. Ltd." @@ -3135,7 +3135,7 @@ "vendor": "Parks S/A Comunicacoes Digitais" }, "1047": { - "vendor": "Elau Ag" + "vendor": "Schneider Electric Automation GmbH" }, "1048": { "vendor": "Teltronic S.A.U." @@ -7563,7 +7563,7 @@ "vendor": "Shenzhen Eastern Digital Tech Ltd." }, "2551": { - "vendor": "SED, a division of Calian" + "vendor": "Calian Advanced Technologies" }, "2552": { "vendor": "Unimo Technology Co., Ltd." @@ -16179,7 +16179,7 @@ "vendor": "Shenzhen Syscan Technology Co.,Ltd." }, "5435": { - "vendor": "EMH metering GmbH & Co. KG" + "vendor": "EMH Metering GmbH & Co. KG" }, "5436": { "vendor": "Kprotech Co., Ltd." @@ -34160,6 +34160,9 @@ "33273": { "vendor": "Texas Instruments" }, + "33568": { + "vendor": "Huawei Device Co., Ltd." + }, "33822": { "vendor": "Cisco Meraki" }, @@ -35849,6 +35852,9 @@ "41937": { "vendor": "Cisco Systems, Inc" }, + "42012": { + "vendor": "Tonly Technology Co. Ltd" + }, "42079": { "vendor": "Huawei Device Co., Ltd." }, @@ -38945,6 +38951,9 @@ "266347": { "vendor": "Xiaomi Communications Co Ltd" }, + "267130": { + "vendor": "Apple, Inc." + }, "267377": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -39140,6 +39149,9 @@ "280271": { "vendor": "Beijing Venustech Cybervision Co.,Ltd." }, + "280327": { + "vendor": "Texas Instruments" + }, "280362": { "vendor": "Palo Alto Networks" }, @@ -39164,6 +39176,9 @@ "281509": { "vendor": "Shenzhen Mercury Communication Technologies Co.,Ltd." }, + "281521": { + "vendor": "Huawei Device Co., Ltd." + }, "281581": { "vendor": "Apple, Inc." }, @@ -39458,6 +39473,9 @@ "300193": { "vendor": "Catch The Wind Inc" }, + "300265": { + "vendor": "FAXedge Technologies, LLC" + }, "300403": { "vendor": "zte corporation" }, @@ -39812,6 +39830,9 @@ "320282": { "vendor": "Sagemcom Broadband SAS" }, + "320391": { + "vendor": "Cisco Systems, Inc" + }, "320593": { "vendor": "Texas Instruments" }, @@ -40388,6 +40409,9 @@ "524805": { "vendor": "Huawei Technologies Co.,Ltd" }, + "524860": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "524942": { "vendor": "Netgear" }, @@ -40913,6 +40937,9 @@ "558237": { "vendor": "Amazon Technologies Inc." }, + "558331": { + "vendor": "Honor Device Co., Ltd." + }, "558427": { "vendor": "Kontron Europe GmbH" }, @@ -41441,6 +41468,9 @@ "787765": { "vendor": "Juniper Systems" }, + "788447": { + "vendor": "Xiaomi Communications Co Ltd" + }, "788660": { "vendor": "HUMAX Co., Ltd." }, @@ -41546,6 +41576,9 @@ "797289": { "vendor": "electric imp, incorporated" }, + "797295": { + "vendor": "Silicon Laboratories" + }, "797318": { "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" }, @@ -41570,6 +41603,9 @@ "799196": { "vendor": "Huawei Technologies Co.,Ltd" }, + "799290": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "800038": { "vendor": "Microsoft Corporation" }, @@ -41891,6 +41927,9 @@ "820517": { "vendor": "Cisco Systems, Inc" }, + "820705": { + "vendor": "Apple, Inc." + }, "820752": { "vendor": "Juniper Networks" }, @@ -42314,6 +42353,9 @@ "846234": { "vendor": "Atls Altec" }, + "846356": { + "vendor": "Ubiquiti Inc" + }, "846537": { "vendor": "ARRIS Group, Inc." }, @@ -42545,6 +42587,9 @@ "1057401": { "vendor": "ZeroDesktop, Inc." }, + "1057665": { + "vendor": "Barrot Technology Co.,LTD" + }, "1057799": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -43004,9 +43049,15 @@ "1085438": { "vendor": "Huawei Technologies Co.,Ltd" }, + "1085565": { + "vendor": "Funshion Online Technologies Co.,Ltd" + }, "1085690": { "vendor": "New H3C Technologies Co., Ltd" }, + "1085692": { + "vendor": "Shenzhen DOOGEE Hengtong Technology CO.,LTD" + }, "1085864": { "vendor": "Espressif Inc." }, @@ -43214,6 +43265,9 @@ "1097852": { "vendor": "Blu-ray Disc Association" }, + "1097941": { + "vendor": "HOLOEYE Photonics AG" + }, "1098098": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -43331,6 +43385,9 @@ "1104457": { "vendor": "Huawei Device Co., Ltd." }, + "1104483": { + "vendor": "Apple, Inc." + }, "1104970": { "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" }, @@ -43565,6 +43622,9 @@ "1315929": { "vendor": "Vodafone Italia S.p.A." }, + "1315965": { + "vendor": "Apple, Inc." + }, "1316070": { "vendor": "Ningbo Sanhe Digital Co.,Ltd" }, @@ -43643,6 +43703,9 @@ "1319922": { "vendor": "Broadcom Limited" }, + "1319923": { + "vendor": "Broadcom Limited" + }, "1320053": { "vendor": "4DReplay, Inc" }, @@ -43943,6 +44006,9 @@ "1338268": { "vendor": "Shenzhen Bilian Electronic Co.,Ltd" }, + "1338407": { + "vendor": "Dongguan Liesheng Electronic Co., Ltd." + }, "1340275": { "vendor": "Tubitak Uekae" }, @@ -44090,6 +44156,9 @@ "1350160": { "vendor": "Microsoft Corporation" }, + "1350307": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "1350447": { "vendor": "JiangSu ZhongXie Intelligent Technology co., LTD" }, @@ -44240,6 +44309,9 @@ "1359201": { "vendor": "Apple, Inc." }, + "1359612": { + "vendor": "Nanjing Jiahao Technology Co., Ltd." + }, "1359934": { "vendor": "ARRIS Group, Inc." }, @@ -45053,6 +45125,9 @@ "1606849": { "vendor": "Guangzhou Shiyuan Electronic Technology Company Limited" }, + "1607223": { + "vendor": "Ingram Micro Services" + }, "1607226": { "vendor": "Digital Art System" }, @@ -45072,7 +45147,7 @@ "vendor": "Samsung Electronics Co.,Ltd" }, "1608096": { - "vendor": "Wuhan Funshion Online Technologies Co.,Ltd" + "vendor": "Funshion Online Technologies Co.,Ltd" }, "1608143": { "vendor": "Tecno Mobile Limited" @@ -45422,6 +45497,9 @@ "1630686": { "vendor": "Chengdu ChipIntelli Technology Co., Ltd" }, + "1630724": { + "vendor": "Beijing Coolshark Technology Co.,Ltd." + }, "1630741": { "vendor": "Nokia" }, @@ -46022,6 +46100,9 @@ "1862053": { "vendor": "BlackBerry RTS" }, + "1862171": { + "vendor": "Ubiquiti Inc" + }, "1862262": { "vendor": "Apple, Inc." }, @@ -46529,6 +46610,9 @@ "1897959": { "vendor": "Turtle Industry Co., Ltd." }, + "1898060": { + "vendor": "Apple, Inc." + }, "1898704": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -46574,6 +46658,9 @@ "2098437": { "vendor": "Radmax Communication Private Limited" }, + "2098614": { + "vendor": "OpenWrt" + }, "2098664": { "vendor": "OOO InProMedia" }, @@ -46805,6 +46892,9 @@ "2112239": { "vendor": "Sivantos GmbH" }, + "2112308": { + "vendor": "Xiaomi Communications Co Ltd" + }, "2112361": { "vendor": "vivo Mobile Communication Co., Ltd." }, @@ -47201,6 +47291,9 @@ "2138481": { "vendor": "Amazon Technologies Inc." }, + "2138624": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "2138852": { "vendor": "Apple, Inc." }, @@ -47321,6 +47414,9 @@ "2146735": { "vendor": "i Wit Digital Co., Limited" }, + "2146992": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "2147215": { "vendor": "Texas Instruments" }, @@ -47402,6 +47498,9 @@ "2153387": { "vendor": "Samsung Electronics Co., Ltd." }, + "2153450": { + "vendor": "Cisco Systems, Inc" + }, "2153619": { "vendor": "Cheetah Hi-Tech, Inc." }, @@ -47660,6 +47759,9 @@ "2364955": { "vendor": "Cisco Systems, Inc" }, + "2365009": { + "vendor": "Chipsea Technologies (Shenzhen) Corp." + }, "2365037": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -47940,7 +48042,7 @@ "vendor": "China Mobile Group Device Co.,Ltd." }, "2384504": { - "vendor": "sysmocom - systems for mobile communications GmbH" + "vendor": "sysmocom - s.f.m.c. GmbH" }, "2384555": { "vendor": "Espressif Inc." @@ -48050,6 +48152,9 @@ "2390520": { "vendor": "KUPSON spol. s r.o." }, + "2391110": { + "vendor": "Flextronics Technologies Mexico S De Rl De Cv" + }, "2391116": { "vendor": "Herman Miller" }, @@ -48146,6 +48251,9 @@ "2399530": { "vendor": "LinkData Technology (Tianjin) Co., LTD" }, + "2399869": { + "vendor": "Beijing Roborock Technology Co., Ltd." + }, "2399915": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -48518,6 +48626,9 @@ "2421469": { "vendor": "Radiant Zemax LLC" }, + "2421510": { + "vendor": "Itel Mobile Limited" + }, "2422142": { "vendor": "Hwh Co., Ltd." }, @@ -48734,6 +48845,9 @@ "2633087": { "vendor": "Apple, Inc." }, + "2633353": { + "vendor": "Wistron Neweb Corporation" + }, "2633666": { "vendor": "Automotive Data Solutions" }, @@ -48956,6 +49070,9 @@ "2648885": { "vendor": "Intel Corporate" }, + "2648924": { + "vendor": "Cisco Systems, Inc" + }, "2649095": { "vendor": "XIAOMI Electronics,CO.,LTD" }, @@ -49202,6 +49319,9 @@ "2666250": { "vendor": "Sirius XM Radio Inc" }, + "2666273": { + "vendor": "zte corporation" + }, "2666306": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -49229,6 +49349,9 @@ "2667771": { "vendor": "Sprocomm Technologies CO.,LTD." }, + "2667921": { + "vendor": "Cisco Systems, Inc" + }, "2668008": { "vendor": "Texas Instruments" }, @@ -50414,6 +50537,9 @@ "2930376": { "vendor": "Raisecom Technology CO., LTD" }, + "2930593": { + "vendor": "Huawei Device Co., Ltd." + }, "2930925": { "vendor": "SonicWall" }, @@ -50471,6 +50597,9 @@ "2935308": { "vendor": "Withus Planet" }, + "2935318": { + "vendor": "Apple, Inc." + }, "2935413": { "vendor": "Robert Bosch GmbH AnP" }, @@ -50567,6 +50696,9 @@ "2941608": { "vendor": "DeviceDesign" }, + "2941657": { + "vendor": "Huawei Device Co., Ltd." + }, "2941712": { "vendor": "Stratacache" }, @@ -50588,6 +50720,9 @@ "2944247": { "vendor": "Shenzhen SDMC Technology CO.,Ltd." }, + "2944393": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "2944432": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -50780,6 +50915,9 @@ "3154788": { "vendor": "Nokia Shanghai Bell Co., Ltd." }, + "3154874": { + "vendor": "Accelerated Memory Production Inc." + }, "3154994": { "vendor": "Intel Corporate" }, @@ -50810,6 +50948,9 @@ "3157932": { "vendor": "Zhejiang HuaRay Technology Co.,Ltd" }, + "3158224": { + "vendor": "Texas Instruments" + }, "3158265": { "vendor": "Espressif Inc." }, @@ -51251,6 +51392,9 @@ "3186205": { "vendor": "Ohsung" }, + "3186274": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "3186683": { "vendor": "Ardomus Networks Corporation" }, @@ -51692,6 +51836,9 @@ "3411814": { "vendor": "Web Sensing LLC" }, + "3412061": { + "vendor": "Texas Instruments" + }, "3412212": { "vendor": "Silicon Laboratories" }, @@ -51872,6 +52019,9 @@ "3422752": { "vendor": "Hewlett Packard Enterprise" }, + "3423615": { + "vendor": "Klipsch Group, Inc." + }, "3423640": { "vendor": "JinQianMao Technology Co.,Ltd." }, @@ -52076,6 +52226,9 @@ "3435932": { "vendor": "Carrier Corporation" }, + "3436136": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "3436170": { "vendor": "Ecosense" }, @@ -52247,6 +52400,9 @@ "3446235": { "vendor": "Logitec Corporation" }, + "3446385": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "3446386": { "vendor": "Tp-Link Technologies Co.,Ltd." }, @@ -52499,6 +52655,9 @@ "3460461": { "vendor": "CommSky Technologies" }, + "3460528": { + "vendor": "Espressif Inc." + }, "3460542": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -52871,6 +53030,9 @@ "3676754": { "vendor": "Seiko Epson Corporation" }, + "3677086": { + "vendor": "Qingdao Intelligent&Precise Electronics Co.,Ltd." + }, "3677210": { "vendor": "Cisco Systems, Inc" }, @@ -52931,6 +53093,9 @@ "3679949": { "vendor": "Andtek" }, + "3680309": { + "vendor": "zte corporation" + }, "3680490": { "vendor": "Fujian Netcom Technology Co., LTD" }, @@ -52949,6 +53114,9 @@ "3681354": { "vendor": "ASUSTek COMPUTER INC." }, + "3681509": { + "vendor": "Tuya Smart Inc." + }, "3681745": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -53216,6 +53384,9 @@ "3701308": { "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" }, + "3701452": { + "vendor": "Shenzhen Bilian Electronic Co.,Ltd" + }, "3701505": { "vendor": "Shenzhen MiaoMing Intelligent Technology Co.,Ltd" }, @@ -53585,6 +53756,9 @@ "3726267": { "vendor": "Sunbow Telecom Co., Ltd." }, + "3726901": { + "vendor": "Guangzhou Yuandianhe Communication Technology Co.,Ltd" + }, "3726944": { "vendor": "Mohlenhoff GmbH" }, @@ -54101,6 +54275,9 @@ "3947341": { "vendor": "Toyo Seisakusho Kaisha, Limited" }, + "3947383": { + "vendor": "Apple, Inc." + }, "3947417": { "vendor": "Itel Mobile Limited" }, @@ -54542,6 +54719,9 @@ "3975086": { "vendor": "zte corporation" }, + "3975178": { + "vendor": "vivo Mobile Communication Co., Ltd." + }, "3975210": { "vendor": "Hewlett Packard" }, @@ -54896,6 +55076,9 @@ "4194758": { "vendor": "3Com Europe Ltd" }, + "4195110": { + "vendor": "NXP Semiconductor (Tianjin) LTD." + }, "4195340": { "vendor": "A&T" }, @@ -55629,7 +55812,7 @@ "vendor": "Apple, Inc." }, "4242536": { - "vendor": "Wuhan Funshion Online Technologies Co.,Ltd" + "vendor": "Funshion Online Technologies Co.,Ltd" }, "4242547": { "vendor": "Cronoplast S.L." @@ -55973,6 +56156,9 @@ "4462114": { "vendor": "Microsoft Corporation" }, + "4462224": { + "vendor": "Wuxi Ranke Technology Co., Ltd." + }, "4462330": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -56051,6 +56237,9 @@ "4466478": { "vendor": "Huawei Device Co., Ltd." }, + "4466501": { + "vendor": "LG Innotek" + }, "4466675": { "vendor": "70mai Co.,Ltd." }, @@ -56435,6 +56624,9 @@ "4491043": { "vendor": "Hoya Service Corporation" }, + "4491107": { + "vendor": "FN-LINK TECHNOLOGY Ltd." + }, "4491227": { "vendor": "Tymphany Acoustic Technology (Huizhou) Co., Ltd." }, @@ -56951,6 +57143,9 @@ "4719146": { "vendor": "B-Link Electronic Limited" }, + "4719156": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "4719238": { "vendor": "Realme Chongqing Mobile Telecommunications Corp.,Ltd." }, @@ -57599,6 +57794,9 @@ "4759800": { "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" }, + "4759920": { + "vendor": "Cisco Systems, Inc" + }, "4759957": { "vendor": "Apple, Inc." }, @@ -58610,6 +58808,9 @@ "5019274": { "vendor": "Wacom Co.,Ltd." }, + "5019553": { + "vendor": "Silicon Laboratories" + }, "5019596": { "vendor": "Apple, Inc." }, @@ -58886,6 +59087,9 @@ "5035958": { "vendor": "Helmer Scientific" }, + "5035976": { + "vendor": "Guangzhou V-Solution Telecommunication Technology Co.,Ltd." + }, "5036431": { "vendor": "Dell Inc." }, @@ -59174,6 +59378,9 @@ "5252752": { "vendor": "Fujitsu Limited" }, + "5252818": { + "vendor": "AVIRE Trading Limited" + }, "5252847": { "vendor": "Murata Manufacturing Co., Ltd." }, @@ -59678,6 +59885,9 @@ "5278897": { "vendor": "Texas Instruments" }, + "5278921": { + "vendor": "zte corporation" + }, "5278965": { "vendor": "China Mobile Group Device Co.,Ltd." }, @@ -60065,6 +60275,9 @@ "5304929": { "vendor": "Photon Sail Technologies" }, + "5304933": { + "vendor": "Apple, Inc." + }, "5305404": { "vendor": "Leeo Inc" }, @@ -60392,6 +60605,9 @@ "5520695": { "vendor": "Ruckus Wireless" }, + "5520736": { + "vendor": "Arcadyan Corporation" + }, "5520786": { "vendor": "Wireless-Tek Technology Limited" }, @@ -61097,6 +61313,9 @@ "5569086": { "vendor": "Samsung Electronics Co.,Ltd" }, + "5569161": { + "vendor": "Medtronic CRM" + }, "5569174": { "vendor": "Nokia Solutions and Networks GmbH & Co. KG" }, @@ -61109,6 +61328,9 @@ "5569983": { "vendor": "Scheidt & Bachmann GmbH" }, + "5570283": { + "vendor": "Texas Instruments" + }, "5570434": { "vendor": "Davit Solution co." }, @@ -61199,6 +61421,9 @@ "5774808": { "vendor": "Sagemcom Broadband SAS" }, + "5775129": { + "vendor": "EMH Metering GmbH & Co. KG" + }, "5775144": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -61487,6 +61712,9 @@ "5797493": { "vendor": "Beijing ECHO Technologies Co.,Ltd" }, + "5797532": { + "vendor": "Palo Alto Networks" + }, "5797548": { "vendor": "Sernet (Suzhou) Technologies Corporation" }, @@ -61496,6 +61724,9 @@ "5797573": { "vendor": "Digi I'S Ltd" }, + "5798368": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "5798477": { "vendor": "Stonesoft Corporation" }, @@ -61736,6 +61967,9 @@ "5813293": { "vendor": "YSTen Technology Co.,Ltd" }, + "5813435": { + "vendor": "Ruijie Networks Co.,LTD" + }, "5813608": { "vendor": "Securitas Direct España, Sau" }, @@ -61838,6 +62072,9 @@ "5820559": { "vendor": "IEEE 1904.1 Working Group" }, + "5820762": { + "vendor": "Texas Instruments" + }, "5820983": { "vendor": "Sichuan Tianyi Comheart Telecom Co.,LTD" }, @@ -61877,6 +62114,9 @@ "5822933": { "vendor": "Tenda Technology Co.,Ltd.Dongguan branch" }, + "5823241": { + "vendor": "Quectel Wireless Solutions Co.,Ltd." + }, "5823253": { "vendor": "Tecno Mobile Limited" }, @@ -61997,6 +62237,9 @@ "5831880": { "vendor": "LenelS2 Carrier" }, + "5831907": { + "vendor": "Funshion Online Technologies Co.,Ltd" + }, "5831968": { "vendor": "Systemhouse Solutions AB" }, @@ -62105,6 +62348,9 @@ "6034709": { "vendor": "Advan" }, + "6034885": { + "vendor": "Shenzhen SSC Technology Co. Ltd" + }, "6034913": { "vendor": "Aidc Technology (S) Pte Ltd" }, @@ -62579,6 +62825,9 @@ "6064696": { "vendor": "Hewlett Packard" }, + "6064814": { + "vendor": "China Dragon Technology Limited" + }, "6065003": { "vendor": "Amazon Technologies Inc." }, @@ -63290,6 +63539,9 @@ "6304059": { "vendor": "Sunnovo International Limited" }, + "6304146": { + "vendor": "OVT India pvt Ltd" + }, "6304151": { "vendor": "Zyxel Communications Corporation" }, @@ -63605,6 +63857,9 @@ "6323933": { "vendor": "Microsoft Mobile Oy" }, + "6324171": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "6324523": { "vendor": "Astronics Custom Control Concepts" }, @@ -63926,6 +64181,9 @@ "6344553": { "vendor": "meerecompany" }, + "6344580": { + "vendor": "ASUSTek COMPUTER INC." + }, "6344748": { "vendor": "Ruckus Wireless" }, @@ -63962,6 +64220,9 @@ "6346011": { "vendor": "Fujitsu Limited" }, + "6346081": { + "vendor": "Shenzhen Glazero Technology Co., Ltd." + }, "6346581": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -64103,6 +64364,9 @@ "6354068": { "vendor": "Hon Hai Precision Ind. Co.,Ltd." }, + "6354249": { + "vendor": "Apple, Inc." + }, "6354332": { "vendor": "CRU-Dataport" }, @@ -64187,6 +64451,9 @@ "6555113": { "vendor": "Shenzhen WayOS Technology Crop., Ltd." }, + "6555532": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "6555638": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -64730,6 +64997,9 @@ "6588913": { "vendor": "Samsung Electronics Co.,Ltd" }, + "6589339": { + "vendor": "Always On Tech Pte.Ltd." + }, "6589627": { "vendor": "Texas Instruments" }, @@ -64787,6 +65057,9 @@ "6593678": { "vendor": "Texas Instruments" }, + "6593779": { + "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" + }, "6593945": { "vendor": "Fs Com Inc" }, @@ -65522,6 +65795,9 @@ "6836317": { "vendor": "Halo Technologies" }, + "6836532": { + "vendor": "Hewlett Packard Enterprise" + }, "6836663": { "vendor": "PowerCloud Systems, Inc." }, @@ -65972,6 +66248,9 @@ "6864457": { "vendor": "Nebula Matrix" }, + "6864748": { + "vendor": "Arista Networks" + }, "6864836": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -66362,6 +66641,9 @@ "7085169": { "vendor": "Zhejiang Dahua Technology Co., Ltd." }, + "7085356": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "7085547": { "vendor": "u-blox AG" }, @@ -66773,6 +67055,9 @@ "7111166": { "vendor": "Mitsuba Corporation" }, + "7111235": { + "vendor": "Huawei Device Co., Ltd." + }, "7111478": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -66998,6 +67283,9 @@ "7123729": { "vendor": "Shenzhen Lianrui Electronics Co.,Ltd" }, + "7123789": { + "vendor": "SharkNinja Operating LLC" + }, "7123792": { "vendor": "Anhui comhigher tech co.,ltd" }, @@ -67088,6 +67376,9 @@ "7130937": { "vendor": "Guangdong Starfive Technology Co., Ltd." }, + "7131144": { + "vendor": "zte corporation" + }, "7131186": { "vendor": "LG Electronics" }, @@ -67514,9 +67805,15 @@ "7353609": { "vendor": "Cisco Systems, Inc" }, + "7353723": { + "vendor": "vivo Mobile Communication Co., Ltd." + }, "7354034": { "vendor": "Focusai Corp" }, + "7354254": { + "vendor": "Tianyi Telecom Terminals Company Limited" + }, "7354385": { "vendor": "Siemens Mobility Limited" }, @@ -68384,6 +68681,9 @@ "7403815": { "vendor": "Samsung Electronics Co.,Ltd" }, + "7403850": { + "vendor": "Apple, Inc." + }, "7403885": { "vendor": "Hangzhou H3C Technologies Co., Limited" }, @@ -68408,6 +68708,9 @@ "7405430": { "vendor": "Texas Instruments" }, + "7602913": { + "vendor": "Texas Instruments" + }, "7603133": { "vendor": "Buffalo.Inc" }, @@ -68459,6 +68762,9 @@ "7606706": { "vendor": "Cisco Systems, Inc" }, + "7606803": { + "vendor": "Linksys USA, Inc" + }, "7606963": { "vendor": "Chongqing Fugui Electronics Co.,Ltd." }, @@ -68537,6 +68843,9 @@ "7612503": { "vendor": "Mayfield Robotics" }, + "7612786": { + "vendor": "Juniper Networks" + }, "7612847": { "vendor": "Hon Hai Precision Ind. Co.,Ltd." }, @@ -69125,6 +69434,9 @@ "7644556": { "vendor": "Texas Instruments" }, + "7644610": { + "vendor": "KYOCERA Corporation" + }, "7644877": { "vendor": "Apple, Inc." }, @@ -69245,6 +69557,9 @@ "7653230": { "vendor": "Rtk-Technologies, Llc" }, + "7653647": { + "vendor": "Microchip Technologies Inc" + }, "7653673": { "vendor": "Zhejiang Dahua Technology Co., Ltd." }, @@ -69560,6 +69875,9 @@ "7866168": { "vendor": "Z.U.K. Elzab S.A." }, + "7866255": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "7866445": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -69683,6 +70001,9 @@ "7874762": { "vendor": "Sonos, Inc." }, + "7874989": { + "vendor": "Ningbo Qixiang Information Technology Co., Ltd" + }, "7875053": { "vendor": "Askey Computer Corp" }, @@ -69746,6 +70067,9 @@ "7877199": { "vendor": "Millennium Group, Inc." }, + "7877641": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "7877766": { "vendor": "Nokia" }, @@ -70175,6 +70499,9 @@ "7903485": { "vendor": "Q9 Networks Inc." }, + "7903506": { + "vendor": "Flyingvoice (HongKong) Technologies Limited" + }, "7903580": { "vendor": "Nationz Technologies Inc" }, @@ -70322,6 +70649,9 @@ "7911148": { "vendor": "Scuf Gaming International LLC" }, + "7911166": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "7911450": { "vendor": "Inter Sales A/S" }, @@ -70547,6 +70877,9 @@ "7922688": { "vendor": "Hon Hai Precision Ind. Co.,Ltd." }, + "7923228": { + "vendor": "Honor Device Co., Ltd." + }, "7923665": { "vendor": "Hewlett Packard" }, @@ -70577,6 +70910,9 @@ "7925180": { "vendor": "OnePlus Technology (Shenzhen) Co., Ltd" }, + "7925324": { + "vendor": "Espressif Inc." + }, "7925580": { "vendor": "Unetconvergence Co., Ltd." }, @@ -70886,6 +71222,9 @@ "8137697": { "vendor": "Shenzhen Ferex Electrical Co.,Ltd" }, + "8137831": { + "vendor": "Espressif Inc." + }, "8137971": { "vendor": "Secure Electrans Ltd" }, @@ -71093,6 +71432,9 @@ "8153003": { "vendor": "Roku, Inc" }, + "8153273": { + "vendor": "Huawei Device Co., Ltd." + }, "8153451": { "vendor": "Atmosic Technologies" }, @@ -71165,6 +71507,9 @@ "8155876": { "vendor": "Unikey Technologies" }, + "8155879": { + "vendor": "Texas Instruments" + }, "8156043": { "vendor": "Cocoon Alarm Ltd" }, @@ -71345,6 +71690,9 @@ "8168521": { "vendor": "Xiaomi Communications Co Ltd" }, + "8168847": { + "vendor": "shenzhen Qikai Electronic Co., Ltd." + }, "8168989": { "vendor": "Mhl, Llc" }, @@ -71354,6 +71702,9 @@ "8169392": { "vendor": "Shenzhen Bilian Electronic Co.,Ltd" }, + "8169708": { + "vendor": "Hewlett Packard Enterprise" + }, "8169835": { "vendor": "Syrotech Networks. Ltd." }, @@ -71435,6 +71786,9 @@ "8174272": { "vendor": "EVBox BV" }, + "8174278": { + "vendor": "Solar Manager AG" + }, "8174447": { "vendor": "Cosco Electronics Co., Ltd." }, @@ -71558,6 +71912,9 @@ "8180709": { "vendor": "Huawei Technologies Co.,Ltd" }, + "8180813": { + "vendor": "Shanghai Moorewatt Energy Technology Co.,Ltd" + }, "8181094": { "vendor": "Amazon Technologies Inc." }, @@ -71636,6 +71993,9 @@ "8185195": { "vendor": "ESEN Optoelectronics Technology Co.,Ltd." }, + "8185618": { + "vendor": "Quectel Wireless Solutions Co.,Ltd." + }, "8185983": { "vendor": "Sagemcom Broadband SAS" }, @@ -71786,6 +72146,9 @@ "8389727": { "vendor": "Apple, Inc." }, + "8389912": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "8389946": { "vendor": "CHeKT Inc." }, @@ -72987,7 +73350,7 @@ "vendor": "SkyHawke Technologies, LLC" }, "8663464": { - "vendor": "Wuhan Funshion Online Technologies Co.,Ltd" + "vendor": "Funshion Online Technologies Co.,Ltd" }, "8663663": { "vendor": "Guangzhou Ava Electronics Technology Co.,Ltd" @@ -73157,6 +73520,9 @@ "8676573": { "vendor": "Huawei Technologies Co.,Ltd" }, + "8676651": { + "vendor": "Donaldson Company" + }, "8676713": { "vendor": "New H3C Technologies Co., Ltd" }, @@ -73226,6 +73592,9 @@ "8681336": { "vendor": "Cochlear Limited" }, + "8681544": { + "vendor": "Ubiquiti Inc" + }, "8681611": { "vendor": "Apple, Inc." }, @@ -73355,6 +73724,9 @@ "8687626": { "vendor": "Arcadyan Corporation" }, + "8688357": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "8688396": { "vendor": "InCoax Networks Europe AB" }, @@ -73403,6 +73775,9 @@ "8691141": { "vendor": "Centera Photonics Inc." }, + "8691286": { + "vendor": "Cloud Network Technology Singapore Pte. Ltd." + }, "8691637": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -74930,6 +75305,9 @@ "9177227": { "vendor": "Remote Solution" }, + "9177258": { + "vendor": "Apple, Inc." + }, "9177588": { "vendor": "ARRIS Group, Inc." }, @@ -74972,6 +75350,9 @@ "9179595": { "vendor": "ABUS Security-Center GmbH & Co. KG" }, + "9179842": { + "vendor": "GLBB Japan" + }, "9180340": { "vendor": "zte corporation" }, @@ -75644,6 +76025,9 @@ "9225652": { "vendor": "Sagemcom Broadband SAS" }, + "9225680": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "9225697": { "vendor": "ShenZhen Konka Telecommunication Technology Co.,Ltd" }, @@ -76001,6 +76385,9 @@ "9442266": { "vendor": "Athom B.V." }, + "9442566": { + "vendor": "Espressif Inc." + }, "9443002": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -76091,6 +76478,9 @@ "9447396": { "vendor": "Apple, Inc." }, + "9447670": { + "vendor": "Wuhan Yangtze Optical Technology CO.,Ltd." + }, "9448174": { "vendor": "Xiaomi Communications Co Ltd" }, @@ -76268,6 +76658,9 @@ "9458758": { "vendor": "Tes Electronic Solutions" }, + "9458871": { + "vendor": "Eltex Enterprise LTD" + }, "9459118": { "vendor": "Ericsson, EAB/RWI/K" }, @@ -76313,6 +76706,9 @@ "9461550": { "vendor": "TCT mobile ltd" }, + "9461626": { + "vendor": "Apple, Inc." + }, "9461645": { "vendor": "modas GmbH" }, @@ -76607,6 +77003,9 @@ "9477668": { "vendor": "ekey biometric systems gmbh" }, + "9477922": { + "vendor": "Zyxel Communications Corporation" + }, "9477939": { "vendor": "EFM Networks" }, @@ -76724,6 +77123,9 @@ "9483910": { "vendor": "Murata Manufacturing Co., Ltd." }, + "9484176": { + "vendor": "Apple, Inc." + }, "9484338": { "vendor": "Extreme Networks Headquarters" }, @@ -76772,6 +77174,9 @@ "9489146": { "vendor": "Google, Inc." }, + "9489315": { + "vendor": "Tecno Mobile Limited" + }, "9489444": { "vendor": "Synaptics, Inc" }, @@ -76982,6 +77387,9 @@ "9501531": { "vendor": "Avaya Inc" }, + "9501533": { + "vendor": "Beijing Xiaomi Mobile Software Co., Ltd" + }, "9501606": { "vendor": "Hon Hai Precision Ind. Co.,Ltd." }, @@ -77078,6 +77486,9 @@ "9702701": { "vendor": "Universal Electronics, Inc." }, + "9702731": { + "vendor": "Cisco Systems, Inc" + }, "9703019": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -77087,6 +77498,9 @@ "9703486": { "vendor": "Belkin International Inc." }, + "9703490": { + "vendor": "Fanox Electronic S.L." + }, "9703898": { "vendor": "ITF Fröschl GmbH" }, @@ -77141,6 +77555,9 @@ "9708398": { "vendor": "Shenzhen Junlan Electronic Ltd" }, + "9708627": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "9708728": { "vendor": "Gree Electric Appliances, Inc. Of Zhuhai" }, @@ -77318,6 +77735,9 @@ "9719628": { "vendor": "Sound United LLC" }, + "9719876": { + "vendor": "Texas Instruments" + }, "9719879": { "vendor": "Rechnerbetriebsgruppe" }, @@ -77687,6 +78107,9 @@ "9745231": { "vendor": "Ruckus Wireless" }, + "9745399": { + "vendor": "Hui Zhou Gaoshengda Technology Co.,LTD" + }, "9745423": { "vendor": "Hewlett Packard Enterprise" }, @@ -78119,6 +78542,9 @@ "9963309": { "vendor": "Texas Instruments" }, + "9963522": { + "vendor": "Orbis Bv" + }, "9963983": { "vendor": "OnePlus Technology (Shenzhen) Co., Ltd" }, @@ -78321,7 +78747,7 @@ "vendor": "Huawei Technologies Co.,Ltd" }, "9977702": { - "vendor": "Wuhan Funshion Online Technologies Co.,Ltd" + "vendor": "Funshion Online Technologies Co.,Ltd" }, "9977759": { "vendor": "China SSJ (Suzhou) Network Technology Inc." @@ -78584,6 +79010,9 @@ "9996100": { "vendor": "Wuxi Hongda Science and Technology Co.,LTD" }, + "9996108": { + "vendor": "Servercom (India) Private Limited" + }, "9996140": { "vendor": "Huawei Device Co., Ltd." }, @@ -79127,6 +79556,9 @@ "10227274": { "vendor": "Shenzhen Vastking Electronic Co.,Ltd." }, + "10227281": { + "vendor": "Schneider Electric" + }, "10228257": { "vendor": "Sichuan AI-Link Technology Co., Ltd." }, @@ -79790,6 +80222,9 @@ "10266269": { "vendor": "Whaley Technology Co.Ltd" }, + "10266328": { + "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" + }, "10267108": { "vendor": "zte corporation" }, @@ -79919,6 +80354,9 @@ "10276226": { "vendor": "Cheng Uei Precision Industry Co.,Ltd" }, + "10276488": { + "vendor": "Ruijie Networks Co.,LTD" + }, "10277328": { "vendor": "Guangzhou Ronsuo Electronic Technology Co.,Ltd" }, @@ -79961,6 +80399,9 @@ "10279486": { "vendor": "Intel Corporate" }, + "10279592": { + "vendor": "Apple, Inc." + }, "10279687": { "vendor": "Yellowtec GmbH" }, @@ -79968,7 +80409,7 @@ "vendor": "Huawei Technologies Co.,Ltd" }, "10279883": { - "vendor": "Wuhan Funshion Online Technologies Co.,Ltd" + "vendor": "Funshion Online Technologies Co.,Ltd" }, "10280049": { "vendor": "Hewlett Packard Enterprise" @@ -80165,6 +80606,9 @@ "10488301": { "vendor": "Avaya Inc" }, + "10488474": { + "vendor": "Huawei Device Co., Ltd." + }, "10488511": { "vendor": "Wieson Technologies Co., Ltd." }, @@ -80174,6 +80618,9 @@ "10488993": { "vendor": "SKTB SKiT" }, + "10489496": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "10489655": { "vendor": "Cisco Systems, Inc" }, @@ -80417,6 +80864,9 @@ "10503770": { "vendor": "Motorola Mobility LLC, a Lenovo Company" }, + "10504091": { + "vendor": "PROCITEC GmbH" + }, "10504151": { "vendor": "Best IT World (India) Pvt Ltd" }, @@ -80834,6 +81284,9 @@ "10529897": { "vendor": "Samsung Electronics Co.,Ltd" }, + "10530146": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "10530209": { "vendor": "JMR Electronics, Inc" }, @@ -81335,6 +81788,9 @@ "10758327": { "vendor": "Yangtze Memory Technologies Co., Ltd." }, + "10758402": { + "vendor": "Hangzhou Hikvision Digital Technology Co.,Ltd." + }, "10758464": { "vendor": "Shenzhen YOUHUA Technology Co., Ltd" }, @@ -82901,6 +83357,9 @@ "11052949": { "vendor": "Hon Hai Precision Ind. Co.,Ltd." }, + "11053331": { + "vendor": "GN Hearing A/S" + }, "11053692": { "vendor": "Huawei Device Co., Ltd." }, @@ -82952,6 +83411,9 @@ "11057587": { "vendor": "Essys" }, + "11057701": { + "vendor": "Hewlett Packard Enterprise" + }, "11057769": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -83636,6 +84098,9 @@ "11295484": { "vendor": "Intel Corporate" }, + "11295788": { + "vendor": "Apple, Inc." + }, "11296016": { "vendor": "Pace Americas" }, @@ -84440,6 +84905,9 @@ "11540614": { "vendor": "SmarDTV Corporation" }, + "11540769": { + "vendor": "Tp-Link Corporation Pte. Ltd." + }, "11540934": { "vendor": "Apple, Inc." }, @@ -84554,6 +85022,9 @@ "11549390": { "vendor": "Huawei Device Co., Ltd." }, + "11549467": { + "vendor": "Kontrolnext Technology (Beijing) Ltd." + }, "11549916": { "vendor": "Intel Corporate" }, @@ -84719,6 +85190,9 @@ "11559367": { "vendor": "Ericsson-LG Enterprise" }, + "11560250": { + "vendor": "Murata Manufacturing Co., Ltd." + }, "11560291": { "vendor": "Shanghai Railway Communication Factory" }, @@ -85115,6 +85589,9 @@ "11584135": { "vendor": "Vantiva USA LLC" }, + "11584199": { + "vendor": "Motorola Mobility LLC, a Lenovo Company" + }, "11584391": { "vendor": "GOEFER, Inc." }, @@ -85490,6 +85967,9 @@ "11804143": { "vendor": "Internet Laboratories, Inc." }, + "11804242": { + "vendor": "Flock Safety" + }, "11804742": { "vendor": "eero inc." }, @@ -85607,6 +86087,9 @@ "11812104": { "vendor": "GX International BV" }, + "11812203": { + "vendor": "NXP Semiconductor (Tianjin) LTD." + }, "11812274": { "vendor": "Degreane Horizon" }, @@ -85793,6 +86276,9 @@ "11826446": { "vendor": "Belkin International Inc." }, + "11826852": { + "vendor": "Huawei Device Co., Ltd." + }, "11827016": { "vendor": "Shenzhen Neoway Technology Co.,Ltd." }, @@ -86102,6 +86588,9 @@ "11846908": { "vendor": "Xiaomi Communications Co Ltd" }, + "11846998": { + "vendor": "Shanghai Kenmyond Industrial Network Equipment Co., Ltd" + }, "11847214": { "vendor": "Molex CMS" }, @@ -86219,6 +86708,9 @@ "11854437": { "vendor": "Shenzhen SDMC Technology CO.,Ltd." }, + "11854800": { + "vendor": "LG Electronics" + }, "11854841": { "vendor": "Silicon Laboratories" }, @@ -86972,6 +87464,9 @@ "12099957": { "vendor": "Roku, Inc." }, + "12100189": { + "vendor": "Motorola Mobility LLC, a Lenovo Company" + }, "12100471": { "vendor": "Cisco Systems, Inc" }, @@ -87101,6 +87596,9 @@ "12107651": { "vendor": "Intel Corporate" }, + "12107783": { + "vendor": "tickIoT Inc." + }, "12107877": { "vendor": "Universal Electronics, Inc." }, @@ -87266,6 +87764,9 @@ "12118102": { "vendor": "Apple, Inc." }, + "12118308": { + "vendor": "Mellanox Technologies, Inc." + }, "12118327": { "vendor": "Sonos, Inc." }, @@ -87584,6 +88085,9 @@ "12331222": { "vendor": "Rowley Associates Limited" }, + "12331384": { + "vendor": "Prama Hikvision India Private Limited" + }, "12331883": { "vendor": "Beijing Haier IC Design Co.,Ltd" }, @@ -87638,6 +88142,9 @@ "12333996": { "vendor": "Silicon Laboratories" }, + "12334282": { + "vendor": "Inovance" + }, "12334565": { "vendor": "Hydro Systems Company" }, @@ -87689,6 +88196,9 @@ "12337409": { "vendor": "Shenzhen TINNO Mobile Technology Corp." }, + "12337568": { + "vendor": "zte corporation" + }, "12337804": { "vendor": "Alpsalpine Co,.Ltd" }, @@ -88025,6 +88535,9 @@ "12358437": { "vendor": "Ningbo Joyson Preh Car Connect Co.,Ltd." }, + "12358692": { + "vendor": "TCT mobile ltd" + }, "12359296": { "vendor": "Shenzhen Gongjin Electronics Co.,Lt" }, @@ -88754,6 +89267,9 @@ "12603774": { "vendor": "Hangzhou Hikvision Digital Technology Co.,Ltd." }, + "12603956": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "12604214": { "vendor": "Beijing National Railway Research & Design Institute of Signal & Communication Group Co..Ltd." }, @@ -88778,6 +89294,9 @@ "12606777": { "vendor": "Jiangsu Huitong Group Co.,Ltd." }, + "12606857": { + "vendor": "Espressif Inc." + }, "12607087": { "vendor": "V. Stonkaus firma \"Kodinis Raktas\"" }, @@ -88826,6 +89345,9 @@ "12610389": { "vendor": "Motorola Mobility LLC, a Lenovo Company" }, + "12610572": { + "vendor": "Apple, Inc." + }, "12610575": { "vendor": "Dobbs Stanford" }, @@ -88862,6 +89384,9 @@ "12614018": { "vendor": "TCL King Electrical Appliances(Huizhou)Co.,Ltd" }, + "12614358": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "12614588": { "vendor": "Cisco Systems, Inc" }, @@ -88919,6 +89444,9 @@ "12618155": { "vendor": "ARRIS Group, Inc." }, + "12618336": { + "vendor": "AltoBeam Inc." + }, "12618445": { "vendor": "Guangzhou Shiyuan Electronic Technology Company Limited" }, @@ -89516,6 +90044,9 @@ "12851433": { "vendor": "Samsung Electronics Co.,Ltd" }, + "12851452": { + "vendor": "Cisco Systems, Inc" + }, "12851595": { "vendor": "Dominion Voting Systems Corporation" }, @@ -89906,6 +90437,9 @@ "12873899": { "vendor": "Ruijie Networks Co.,LTD" }, + "12873917": { + "vendor": "Mellanox Technologies, Inc." + }, "12874032": { "vendor": "Fon Technology S.L." }, @@ -89936,6 +90470,9 @@ "12875242": { "vendor": "Huawei Technologies Co.,Ltd" }, + "12875620": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "12875691": { "vendor": "Beijing ASU Tech Co.,Ltd" }, @@ -90860,6 +91397,9 @@ "13131776": { "vendor": "ASUSTek COMPUTER INC." }, + "13131919": { + "vendor": "Cisco Systems, Inc" + }, "13132785": { "vendor": "Sony Interactive Entertainment Inc." }, @@ -90911,6 +91451,9 @@ "13136084": { "vendor": "IBO Technology Co,Ltd" }, + "13136159": { + "vendor": "Suzhou Tesien Technology Co., Ltd." + }, "13136165": { "vendor": "Johnson Outdoors Marine Electronics d/b/a Minnkota" }, @@ -90971,6 +91514,9 @@ "13141063": { "vendor": "Beautiful Enterprise Co., Ltd" }, + "13141070": { + "vendor": "Flextronics International Kft" + }, "13141132": { "vendor": "Ruckus Wireless" }, @@ -91427,6 +91973,9 @@ "13168294": { "vendor": "Shenzhen SHX Technology Co., Ltd" }, + "13168343": { + "vendor": "Lightspeed Technologies Inc." + }, "13168430": { "vendor": "Beijing Gefei Tech. Co., Ltd" }, @@ -92168,6 +92717,9 @@ "13406355": { "vendor": "Hansong Tehnologies" }, + "13406358": { + "vendor": "Huawei Device Co., Ltd." + }, "13406440": { "vendor": "Shenzhen YOUHUA Technology Co., Ltd" }, @@ -92192,6 +92744,9 @@ "13407904": { "vendor": "Huawei Technologies Co.,Ltd" }, + "13407906": { + "vendor": "China Mobile Group Device Co.,Ltd." + }, "13407973": { "vendor": "Dell Inc." }, @@ -92936,6 +93491,9 @@ "13653695": { "vendor": "Amosense" }, + "13653755": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "13653836": { "vendor": "Cisco Systems, Inc" }, @@ -93062,6 +93620,9 @@ "13659000": { "vendor": "Apple, Inc." }, + "13659592": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "13659593": { "vendor": "Sagemcom Broadband SAS" }, @@ -93272,6 +93833,9 @@ "13675784": { "vendor": "HP Inc." }, + "13676037": { + "vendor": "Xiaomi Communications Co Ltd" + }, "13676268": { "vendor": "Alpha Networks Inc." }, @@ -93812,6 +94376,9 @@ "13908740": { "vendor": "Intel Corporate" }, + "13908874": { + "vendor": "Shenzhen Zhide technology Co., LTD" + }, "13909305": { "vendor": "Dialog Semiconductor" }, @@ -94964,6 +95531,9 @@ "14171071": { "vendor": "Intel Corporate" }, + "14171098": { + "vendor": "Espressif Inc." + }, "14171241": { "vendor": "Shenzhen TINNO Mobile Technology Corp." }, @@ -95591,6 +96161,9 @@ "14209483": { "vendor": "Apple, Inc." }, + "14209633": { + "vendor": "GD Midea Air-Conditioning Equipment Co.,Ltd." + }, "14209660": { "vendor": "Jema Energy, Sa" }, @@ -96230,6 +96803,9 @@ "14444779": { "vendor": "Nintendo Co.,Ltd" }, + "14444981": { + "vendor": "eero inc." + }, "14445026": { "vendor": "Samsung Electronics Co.,Ltd" }, @@ -96752,6 +97328,9 @@ "14478055": { "vendor": "D-Link Corporation" }, + "14478157": { + "vendor": "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd." + }, "14478163": { "vendor": "Wuhan QianXiao Elecronic Technology CO.,LTD" }, @@ -96956,6 +97535,9 @@ "14687297": { "vendor": "Extreme Networks Headquarters" }, + "14687399": { + "vendor": "Arista Networks, Inc." + }, "14687470": { "vendor": "Bravo Tech, Inc." }, @@ -98243,6 +98825,9 @@ "14966327": { "vendor": "Intel Corporate" }, + "14966476": { + "vendor": "Juniper Networks" + }, "14966529": { "vendor": "Raspberry Pi Trading Ltd" }, @@ -98447,6 +99032,9 @@ "14979626": { "vendor": "Dbg Holdings Limited" }, + "14979714": { + "vendor": "Samsung Electronics Co.,Ltd" + }, "14979815": { "vendor": "Gridlink Tech. Co.,Ltd." }, @@ -98952,7 +99540,7 @@ "vendor": "Shenzhen Vipstech Co., Ltd" }, "15210492": { - "vendor": "Fujitsu Cloud Technologies Limited" + "vendor": "Fujitsu Limited" }, "15211096": { "vendor": "Technologic Systems" @@ -98981,6 +99569,9 @@ "15212770": { "vendor": "HUMAX Co., Ltd." }, + "15213185": { + "vendor": "GD Midea Air-Conditioning Equipment Co.,Ltd." + }, "15213240": { "vendor": "Shenzhen Skyworth Digital Technology CO., Ltd" }, @@ -99122,6 +99713,9 @@ "15223380": { "vendor": "Beijing Xiaomi Mobile Software Co., Ltd" }, + "15223416": { + "vendor": "Apple, Inc." + }, "15223882": { "vendor": "Amazon Technologies Inc." }, @@ -99587,6 +100181,9 @@ "15251234": { "vendor": "GreenTrol Automation" }, + "15251235": { + "vendor": "Shenzhen Vatilon Electronics Co.,Ltd" + }, "15251272": { "vendor": "Cisco Systems, Inc" }, @@ -99995,6 +100592,9 @@ "15468773": { "vendor": "Motorola Mobility LLC, a Lenovo Company" }, + "15469001": { + "vendor": "Texas Instruments" + }, "15469486": { "vendor": "Hangzhou BroadLink Technology Co.,Ltd" }, @@ -100379,6 +100979,9 @@ "15495643": { "vendor": "Reolink Innovation Limited" }, + "15495771": { + "vendor": "zte corporation" + }, "15496057": { "vendor": "Apple, Inc." }, @@ -100961,6 +101564,9 @@ "15730929": { "vendor": "Samsung Electronics Co.,Ltd" }, + "15730957": { + "vendor": "Tp-Link Corporation Pte. Ltd." + }, "15732060": { "vendor": "JinQianMao Technology Co.,Ltd." }, @@ -101561,6 +102167,9 @@ "15772500": { "vendor": "Mitsumi Electric Co.,Ltd." }, + "15772659": { + "vendor": "Fiberhome Telecommunication Technologies Co.,LTD" + }, "15772836": { "vendor": "HBC-radiomatic" }, @@ -101717,6 +102326,9 @@ "15781169": { "vendor": "Samsung Electronics Co.,Ltd" }, + "15781709": { + "vendor": "BitRecords GmbH" + }, "15782028": { "vendor": "TCT mobile ltd" }, @@ -101756,6 +102368,9 @@ "15783359": { "vendor": "Intel Corporate" }, + "15783477": { + "vendor": "Apple, Inc." + }, "15783511": { "vendor": "Echosens" }, @@ -101771,6 +102386,9 @@ "15783900": { "vendor": "Wesine (Wuhan) Technology Co., Ltd." }, + "15783918": { + "vendor": "Huawei Device Co., Ltd." + }, "15783941": { "vendor": "Cisco Systems, Inc" }, @@ -102317,6 +102935,9 @@ "16013378": { "vendor": "Boxx TV Ltd" }, + "16014121": { + "vendor": "Huawei Technologies Co.,Ltd" + }, "16014195": { "vendor": "Wanjiaan Interconnected Technology Co., Ltd" }, @@ -102563,6 +103184,9 @@ "16031114": { "vendor": "Fantasia Trading LLC" }, + "16031438": { + "vendor": "Sena Technologies Co., Ltd." + }, "16031471": { "vendor": "Taicang T&W Electronics" }, @@ -102578,6 +103202,9 @@ "16032404": { "vendor": "Eagle World Development Co., Limited" }, + "16032528": { + "vendor": "Apple, Inc." + }, "16032885": { "vendor": "Intel Corporate" }, @@ -102776,6 +103403,9 @@ "16044082": { "vendor": "Yunnan Ideal Information&Technology.,Ltd" }, + "16044199": { + "vendor": "Zhejiang Weilai Jingling Artificial Intelligence Technology Co., Ltd." + }, "16044296": { "vendor": "Intel Corporate" }, @@ -104123,6 +104753,9 @@ "16315698": { "vendor": "ARRIS Group, Inc." }, + "16315788": { + "vendor": "Apple, Inc." + }, "16316345": { "vendor": "Huawei Technologies Co.,Ltd" }, @@ -104951,6 +105584,9 @@ "16559375": { "vendor": "Qts Networks" }, + "16559659": { + "vendor": "Titan Products Ltd." + }, "16559668": { "vendor": "ARRIS Group, Inc." }, @@ -104975,6 +105611,9 @@ "16560854": { "vendor": "Cig Shanghai Co Ltd" }, + "16561031": { + "vendor": "Leapmotor (Jinhua) New Energy Vehicle Parts Technology Co., Ltd." + }, "16561084": { "vendor": "Intel Corporate" }, @@ -107149,6 +107788,30 @@ } ] }, + "3425891": { + "vendor": "", + "maskedFilters": [ + { + "mask": 28, + "vendors": { + "57476913299456": "China Drive Electric Co.,Ltd(Zhe Jiang)", + "57476914348032": "Chain Reaction Ltd", + "57476915396608": "Amcrest Technologies", + "57476916445184": "LA Clippers", + "57476917493760": "Luminys Systems Corporation", + "57476918542336": "Wuhan IDXLINK Technology Co., Ltd", + "57476919590912": "China Motor Corporation", + "57476920639488": "Shenzhen Htfuture Co., Ltd", + "57476921688064": "Bluesoo Tech (HongKong) Co.,Limited", + "57476922736640": "Shenzhen C & D Electronics Co., Ltd.", + "57476923785216": "Shenzhen Shenhong Communication Technology Co., Ltd", + "57476924833792": "Grohe AG", + "57476925882368": "mirle automation corporation", + "57476926930944": "Hangzhou Tashi Internet Of Things Technology Co., Ltd" + } + } + ] + }, "3459286": { "vendor": "", "maskedFilters": [ @@ -108547,6 +109210,31 @@ } ] }, + "6052428": { + "vendor": "", + "maskedFilters": [ + { + "mask": 28, + "vendors": { + "101542891880448": "Jinchuan Group Co.,Ltd", + "101542892929024": "Orchid Products Limited", + "101542893977600": "ITS Partner (O.B.S) S.L.", + "101542895026176": "Ferroamp AB (publ)", + "101542896074752": "Yihua Communications(Huizhou)Co.,Ltd", + "101542897123328": "Annapurna labs", + "101542898171904": "Shenzhen Sunsoont Technology Co.,Ltd", + "101542899220480": "Ace Computers", + "101542900269056": "Spot AI, Inc.", + "101542901317632": "Linktech Systerm Technology Co.,Ltd", + "101542902366208": "Chengdu Skysoft Info&Tech Co.,Ltd.", + "101542903414784": "Esme Solutions", + "101542904463360": "tarm AG", + "101542905511936": "Aeva, Inc.", + "101542906560512": "Ai-Rider Corporation" + } + } + ] + }, "6056684": { "vendor": "", "maskedFilters": [ @@ -108605,14 +109293,19 @@ "vendors": { "101738514219008": "Nanjing Shufan Information Technology Co., Ltd.", "101738515267584": "COMET SYSTEM, s.r.o.", + "101738516316160": "Freeus LLC", "101738517364736": "Shenzhen Beiens Import and Export Co.,Ltd", "101738518413312": "Telfi Technologies Private Limited", "101738519461888": "Hangzhou Advanced Intelligent Manufacturing Systems Co.,Ltd.", + "101738520510464": "Shanghai Jianyi Technology Co., Ltd", "101738521559040": "Tradewinds Networks Incorporated", "101738522607616": "fmad engineering", "101738523656192": "Annapurna labs", "101738524704768": "Piscis Networks Private Limited", - "101738526801920": "Dragonglass Technology(Shenzhen)Co.,Ltd." + "101738525753344": "Credo Diagnostics Biomedical Pte. Ltd. Taiwan branch(SINGAPORE)", + "101738526801920": "Dragonglass Technology(Shenzhen)Co.,Ltd.", + "101738527850496": "Zilia Technologies", + "101738528899072": "Beijing Townsky Technology Co.,Ltd" } } ] @@ -113387,6 +114080,31 @@ } ] }, + "15489997": { + "vendor": "", + "maskedFilters": [ + { + "mask": 28, + "vendors": { + "259879025508352": "Dong Guan Yung Fu Electronics Ltd.", + "259879026556928": "Ingersoll Rand", + "259879027605504": "Sfera Labs S.r.l.", + "259879028654080": "Hefei BOE Vision-electronic Technology Co.,Ltd.", + "259879029702656": "Green Solutions (Chengdu) Co., Ltd", + "259879030751232": "Shenzhen Qunfang Technology Co., LTD.", + "259879031799808": "Jiangsu Fushi Electronic Technology Co., Ltd", + "259879032848384": "Annapurna labs", + "259879033896960": "Doosan Bobcat North America", + "259879034945536": "C&D Technologies", + "259879035994112": "CareSix Inc.", + "259879037042688": "StepOver GmbH", + "259879038091264": "Quicklert Inc", + "259879039139840": "ASHIDA Electronics Pvt. Ltd", + "259879040188416": "Autel Robotics USA LLC" + } + } + ] + }, "15505932": { "vendor": "", "maskedFilters": [ @@ -113787,6 +114505,31 @@ } ] }, + "16284217": { + "vendor": "", + "maskedFilters": [ + { + "mask": 28, + "vendors": { + "273203825999872": "Hangzhou Yongxie Technology Co., Ltd", + "273203827048448": "Hangzhou Jiemu Electronic Technology Co.,Ltd", + "273203828097024": "Total-one TECHNOLOGY CO., LTD.", + "273203829145600": "Overview Limited", + "273203830194176": "Zsystem technology co.,", + "273203831242752": "tatwah SA", + "273203832291328": "Annapurna labs", + "273203833339904": "Shenzhen Electron Technology Co., LTD.", + "273203834388480": "Beijing Zhongyuan Yishang Technology Co.,LTD", + "273203835437056": "Shenzhenshilemaikejiyouxiangongsi", + "273203836485632": "Feltrin Industria E Comercio", + "273203837534208": "FuyanshengElectronicFujian Co.ltd", + "273203838582784": "Flextronics International Kft", + "273203839631360": "Xiamen Tonmind Technology Co.,Ltd", + "273203840679936": "Cognosos, Inc." + } + } + ] + }, "16288316": { "vendor": "", "maskedFilters": [ @@ -116395,7 +117138,7 @@ "346861408256": "Bachmann Monitoring GmbH", "346861412352": "TTi Ltd", "346861416448": "IFAM GmbH", - "346861420544": "Internet Protocolo Lógica SL", + "346861420544": "Internet Protocolo Logica Sl", "346861424640": "Peek Traffic Corp", "346861428736": "UltraVision Security Systems, Inc.", "346861432832": "Polygon Informatics Ltd.", @@ -117049,7 +117792,7 @@ "346864087040": "North Bridge Technologies", "346864091136": "Ooo Npf Atis", "346864095232": "Nayos LTD", - "346864099328": "Measurement Technology NW", + "346864099328": "Rugged Controls", "346864103424": "RO.VE.R. Laboratories S.p.A", "346864107520": "FEW Bauer GmbH", "346864111616": "Peek Traffic Corporation", @@ -122820,6 +123563,8 @@ "154066449702912": "Paragraf", "154066449719296": "SCIREQ Scientific Respiratory Equipment Inc", "154066449727488": "Utthunga Techologies Pvt Ltd", + "154066449731584": "Savant Group", + "154066449735680": "Telica Telecom Private Limited", "154066449743872": "Shin Nihon Denshi Co., Ltd.", "154066449747968": "SMITEC S.p.A.", "154066449760256": "eyrise B.V.", @@ -122902,6 +123647,7 @@ "154066450468864": "RealD, Inc.", "154066450472960": "Avd Innovation Limited", "154066450481152": "Power Electronics Espana, S.L.", + "154066450509824": "Leidos", "154066450513920": "Autopharma", "154066450538496": "Cleanwatts Digital, S.A.", "154066450554880": "SmartSky Networks LLC", @@ -122927,6 +123673,7 @@ "154066450722816": "Timberline Manufacturing", "154066450726912": "Sanmina SCI Medical", "154066450731008": "Neuralog LP", + "154066450735104": "Sicon srl", "154066450739200": "Grossenbacher Systeme AG", "154066450743296": "Automata GmbH & Co. KG", "154066450747392": "Foxconn Technology Co., Ltd.", @@ -122966,12 +123713,15 @@ "154066451103744": "Fracarro Radioindustrie Srl", "154066451132416": "Emcom Systems", "154066451140608": "Agrowtek Inc.", + "154066451148800": "Bavaria Digital Technik GmbH", "154066451152896": "Zelp Ltd", "154066451161088": "MI Inc.", + "154066451181568": "NICE Total Cash Management Co., Ltd.", "154066451197952": "Sicon srl", "154066451214336": "M-Pulse GmbH & Co.KG", "154066451247104": "Sicon srl", "154066451251200": "Tiflex", + "154066451255296": "VERIDAS Digital Authentication Solutions S.L", "154066451259392": "Secuinfo Co.Ltd", "154066451263488": "TEKVOX, Inc", "154066451279872": "FeedFlo", @@ -123069,7 +123819,9 @@ "154066452307968": "Roog zhi tong Technology(Beijing) Co.,Ltd", "154066452316160": "Megger Germany GmbH", "154066452344832": "Nagtech Llc", + "154066452348928": "Connected Development", "154066452353024": "Pantherun Technologies Pvt Ltd", + "154066452357120": "Serap", "154066452365312": "YUYAMA MFG Co.,Ltd", "154066452369408": "Nonet Inc", "154066452373504": "Radiation Solutions Inc.", @@ -123105,6 +123857,7 @@ "154066452680704": "DEUTA Werke GmbH", "154066452684800": "Enless Wireless", "154066452697088": "Florida R&D Associates LLC", + "154066452713472": "TSS COMPANY s.r.o.", "154066452721664": "MB connect line GmbH Fernwartungssysteme", "154066452725760": "Unimar, Inc.", "154066452729856": "Enestone Corporation", @@ -123166,6 +123919,7 @@ "154066453221376": "Duevi Srl", "154066453225472": "DIAS Infrared GmbH", "154066453233664": "spar Power Technologies Inc.", + "154066453258240": "SIDUS Solutions, LLC", "154066453262336": "Scarlet Tech Co., Ltd.", "154066453266432": "Yslab", "154066453274624": "Shenzhen ROLSTONE Technology Co., Ltd", @@ -123201,6 +123955,7 @@ "154066453491712": "Ai-Blox", "154066453495808": "Hubris Technologies Private Limited", "154066453508096": "Clausal Computing Oy", + "154066453516288": "Oriux", "154066453532672": "Suzhou Lianshichuangzhi Technology Co., Ltd", "154066453544960": "NavSys Technology Inc.", "154066453549056": "Stratis IOT", @@ -123210,6 +123965,7 @@ "154066453585920": "Mahindr & Mahindra", "154066453594112": "Tripltek", "154066453598208": "EMIT GmbH", + "154066453602304": "UVIRCO Technologies", "154066453610496": "e.p.g. Elettronica s.r.l.", "154066453614592": "FRAKO Kondensatoren- und Anlagenbau GmbH", "154066453630976": "Unlimited Bandwidth LLC", @@ -123251,7 +124007,9 @@ "154066454040576": "Solid State Supplies Ltd", "154066454052864": "MB connect line GmbH Fernwartungssysteme", "154066454056960": "Novanta IMS", + "154066454061056": "Potter Electric Signal Co LLC", "154066454073344": "Figment Design Laboratories", + "154066454093824": "Onbitel", "154066454110208": "GVA Lighting, Inc.", "154066454114304": "RealD, Inc.", "154066454122496": "Guan Show Technologe Co., Ltd.", @@ -123286,6 +124044,7 @@ "154066454450176": "Breas Medical AB", "154066454454272": "Bludigit SpA", "154066454470656": "Alaire Technologies Inc", + "154066454474752": "Potter Electric Signal Co. LLC", "154066454478848": "Exact Sciences", "154066454482944": "Martec Marine S.p.a.", "154066454495232": "Vekto", @@ -123310,6 +124069,7 @@ "154066454745088": "Eers Global Technologies Inc.", "154066454757376": "XOR UK Corporation Limited", "154066454773760": "Tieline Research Pty Ltd", + "154066454777856": "Abbott Diagnostics Technologies AS", "154066454802432": "SmartD Technologies Inc", "154066454810624": "Photonic Science and Engineering Ltd", "154066454814720": "Sanskruti", @@ -123329,6 +124089,7 @@ "154066454933504": "Smart Radar System, Inc", "154066454937600": "Wagner Group GmbH", "154066454974464": "MP-SENSOR GmbH", + "154066454986752": "Aski Industrie Elektronik GmbH", "154066454990848": "United States Technologies Inc.", "154066455011328": "Hiwin Mikrosystem Corp.", "154066455023616": "Cubic ITS, Inc. dba GRIDSMART Technologies", @@ -123360,6 +124121,7 @@ "154066455216128": "Schildknecht AG", "154066455224320": "Hanateksystem", "154066455232512": "Dexter Laundry Inc.", + "154066455236608": "Deep Detection / ESB01736990", "154066455277568": "Avida, Inc.", "154066455281664": "ELTEK SpA", "154066455285760": "Acod", @@ -123371,7 +124133,9 @@ "154066455339008": "NPO ECO-INTECH Ltd.", "154066455343104": "Potter Electric Signal Company", "154066455351296": "ISDI Ltd", + "154066455363584": "SAKURA SEIKI Co., Ltd.", "154066455367680": "SpectraDynamics, Inc.", + "154066455384064": "Shanghai DIDON Industry Co,. Ltd.", "154066455400448": "Hvrnd", "154066455408640": "Quectel Wireless Solutions Co.,Ltd.", "154066455412736": "Ear Micro LLC", @@ -123475,6 +124239,7 @@ "154066456297472": "Thermoeye Inc", "154066456330240": "Lineage Power Pvt Ltd.,", "154066456334336": "Vt100 Srl", + "154066456338432": "Monnit Corporation", "154066456342528": "Elix Systems SA", "154066456354816": "Farmobile LLC", "154066456358912": "MEDIASCOPE Inc.", @@ -123487,11 +124252,12 @@ "154066456403968": "LDA Audiotech", "154066456408064": "Hamamatsu Photonics K.K.", "154066456424448": "Slat", - "154066456432640": "Sanchar Communication Systems", + "154066456432640": "Sanchar Wireless Communications Ltd", "154066456461312": "GuangZhou HOKO Electric CO.,LTD", "154066456481792": "Wende Tan", "154066456485888": "Nexilis Electronics India Pvt Ltd (PICSYS)", "154066456494080": "Hubbell Power Systems", + "154066456498176": "aeroLiFi GmbH", "154066456502272": "Emerson Rosemount Analytical", "154066456506368": "Sontay Ltd.", "154066456510464": "Arcus-EDS GmbH", @@ -123511,6 +124277,7 @@ "154066456645632": "GS Industrie-Elektronik GmbH", "154066456653824": "Season Electronics Ltd", "154066456662016": "IoT Water Analytics S.L.", + "154066456670208": "Automata GmbH & Co. KG", "154066456698880": "Fit", "154066456719360": "GJD Manufacturing", "154066456727552": "Wuhan Xingtuxinke ELectronic Co.,Ltd", @@ -123540,6 +124307,7 @@ "154066456981504": "Onicon", "154066456993792": "OvercomTech", "154066457010176": "Nexion Data Systems P/L", + "154066457022464": "Internet Protocolo Logica Sl", "154066457034752": "Abb", "154066457047040": "Adasky Ltd.", "154066457055232": "Epigon spol. s r.o.", @@ -123549,6 +124317,7 @@ "154066457092096": "Dave Srl", "154066457108480": "Dorlet Sau", "154066457116672": "Antai technology Co.,Ltd", + "154066457120768": "Hills Health Solutions", "154066457137152": "ehoosys Co.,LTD.", "154066457145344": "Video Network Security", "154066457161728": "Vytahy-Vymyslicky s.r.o.", @@ -123569,6 +124338,7 @@ "154066457284608": "Flextronics International Kft", "154066457288704": "Star Systems International Limited", "154066457300992": "Systel Inc", + "154066457313280": "American Energy Storage Innovations", "154066457325568": "ASTRACOM Co. Ltd", "154066457329664": "Q-Light AS", "154066457337856": "Support Professionals B.V.", @@ -123635,7 +124405,9 @@ "154066457882624": "robert juliat", "154066457903104": "Methods2Business B.V.", "154066457911296": "Orange Precision Measurement LLC", + "154066457915392": "SAXOGY POWER ELECTRONICS GmbH", "154066457923584": "AEM Singapore Pte Ltd", + "154066457927680": "AT-Automation Technology GmbH", "154066457935872": "G.M. International srl", "154066457952256": "FleetSafe India Private Limited", "154066457968640": "Mitsubishi Electric Klimat Transportation Systems S.p.A.", @@ -123673,9 +124445,11 @@ "154066458320896": "Abb", "154066458333184": "e.kundenservice Netz GmbH", "154066458337280": "Garten Automation", + "154066458341376": "roda computer GmbH", "154066458345472": "SFERA srl", "154066458357760": "Atlantic Pumps Ltd", "154066458361856": "Zing 5g Communications Canada Inc.", + "154066458370048": "Apen Group S.p.A. (VAT IT08767740155)", "154066458390528": "EngiNe srl", "154066458406912": "Forever Engineering Systems Pvt. Ltd.", "154066458411008": "Shenzhen Peake Technology Co.,Ltd.", @@ -123690,6 +124464,7 @@ "154066458513408": "Flextronics International Kft", "154066458517504": "Tmy Technology Inc.", "154066458521600": "DEUTA-WERKE GmbH", + "154066458550272": "Longoo Limited", "154066458554368": "Taiwan Aulisa Medical Devices Technologies, Inc", "154066458558464": "SAL Navigation AB", "154066458562560": "Pantherun Technologies Pvt Ltd", @@ -123799,6 +124574,7 @@ "154066459594752": "Phe-nX B.V.", "154066459602944": "Syscom Instruments SA", "154066459607040": "PAN Business & Consulting (ANYOS]", + "154066459611136": "Aksel sp. z o.o.", "154066459619328": "Breas Medical AB", "154066459627520": "DB Systel GmbH", "154066459639808": "uHave Control, Inc", @@ -123853,6 +124629,7 @@ "154066460078080": "Satelles Inc", "154066460082176": "BITECHNIK GmbH", "154066460086272": "Guan Show Technologe Co., Ltd.", + "154066460106752": "secutech Co.,Ltd.", "154066460110848": "GJD Manufacturing", "154066460123136": "Shanghai Wise-Tech Intelligent Technology Co.,Ltd.", "154066460135424": "Lumiplan Duhamel", @@ -123896,9 +124673,11 @@ "154066460610560": "3D perception AS", "154066460618752": "EkspertStroyProekt", "154066460622848": "Beijing Wenrise Technology Co., Ltd.", + "154066460631040": "Global Design Tech(ZS) Co.,Ltd", "154066460635136": "Morgen Technology", "154066460643328": "Mitsubishi Electric India Pvt. Ltd.", "154066460676096": "Infinitive Group Limited", + "154066460680192": "Agrology, PBC", "154066460688384": "Future wave ultra tech Company", "154066460700672": "Integer.pl S.A.", "154066460704768": "Jacobs Technology, Inc.", @@ -123978,6 +124757,7 @@ "154066461417472": "Phygitall Soluções Em Internet Das Coisas", "154066461421568": "Lineage Power Pvt Ltd.,", "154066461442048": "Picocom Technology Ltd", + "154066461454336": "Vaunix Technology Corporation", "154066461478912": "Sanchar Telesystems limited", "154066461483008": "Arcvideo", "154066461499392": "YUYAMA MFG Co.,Ltd", @@ -124015,6 +124795,7 @@ "154066461859840": "Grupo Epelsa S.L.", "154066461863936": "Zaruc Tecnologia LTDA", "154066461880320": "Jiangxi Lv C-Chong Charging Technology Co.Ltd", + "154066461888512": "SmartD Technologies Inc", "154066461900800": "Liberator Pty Ltd", "154066461908992": "AirScan, Inc. dba HemaTechnologies", "154066461913088": "Retency", @@ -124027,6 +124808,7 @@ "154066461962240": "A&T Corporation", "154066461966336": "Sound Health Systems", "154066461974528": "BESO sp. z o.o.", + "154066461990912": "attocube systems AG", "154066461995008": "IO Master Technology", "154066462003200": "Pro-Custom Group", "154066462007296": "NOVA Products GmbH", @@ -124084,6 +124866,7 @@ "154066462449664": "Sd Optics", "154066462453760": "Shenzhen Smartlog Technologies Co.,Ltd", "154066462457856": "Sypris Electronics", + "154066462461952": "Flextroincs International (Taiwain Ltd", "154066462482432": "SGi Technology Group Ltd.", "154066462490624": "Lumiplan Duhamel", "154066462507008": "Spacee", @@ -124093,6 +124876,7 @@ "154066462535680": "Strategic Robotic Systems", "154066462543872": "Tunstall A/S", "154066462560256": "Alfa Proxima d.o.o.", + "154066462564352": "YUYAMA MFG Co.,Ltd", "154066462576640": "Beijing Ceresdate Technology Co.,LTD", "154066462580736": "GMI Ltd", "154066462588928": "Ajeco Oy", @@ -124103,6 +124887,7 @@ "154066462642176": "Yaviar LLC", "154066462683136": "Freedom Atlantic", "154066462687232": "MERKLE Schweissanlagen-Technik GmbH", + "154066462691328": "Glasson Electronics Ltd", "154066462703616": "VECOS Europe B.V.", "154066462707712": "Taolink Technologies Corporation", "154066462715904": "Power Electronics Espana, S.L.", @@ -124111,6 +124896,7 @@ "154066462765056": "JW Froehlich Maschinenfabrik GmbH", "154066462773248": "Soehnle Industrial Solutions GmbH", "154066462777344": "EQ Earthquake Ltd.", + "154066462793728": "Smart Data (Shenzhen) Intelligent System Co., Ltd.", "154066462797824": "Magnet-Physik Dr. Steingroever GmbH", "154066462810112": "Infosoft Digital Design and Services P L", "154066462814208": "J.M. Voith SE & Co. KG", @@ -124127,6 +124913,7 @@ "154066462896128": "BRS Sistemas Eletrônicos", "154066462908416": "Dyncir Soluções Tecnológicas Ltda", "154066462920704": "Gamber-Johnson LLC", + "154066462924800": "Rowan Elettronica Srl", "154066462928896": "ARKRAY,Inc.Kyoto Laboratory", "154066462937088": "iC-Haus GmbH", "154066462945280": "Maris Tech Ltd.", @@ -124165,15 +124952,19 @@ "154066463264768": "Minartime(Beijing)Science &Technology Development Co.,Ltd", "154066463285248": "Labforge Inc.", "154066463289344": "Mecco LLC", + "154066463297536": "Benetel", "154066463305728": "EYatsko Individual", "154066463322112": "I.S.A. - Altanova group srl", "154066463338496": "Audiodo International AB", "154066463354880": "Free Talk Engineering Co., Ltd", "154066463358976": "NAS Engineering PRO", "154066463363072": "Ametek Cts Gmbh", + "154066463371264": "PLX Inc.", "154066463375360": "R3 IoT Ltd.", + "154066463387648": "Taiv Inc", "154066463395840": "Secure Bits", "154066463399936": "Anteus Kft.", + "154066463412224": "Eskomar Ltd.", "154066463440896": "Kronotech Srl", "154066463457280": "Cuu Long Technology And Trading Company Limited", "154066463465472": "Applied Materials", @@ -124198,6 +124989,7 @@ "154066463633408": "Mobileye", "154066463657984": "ADiCo Corporation", "154066463670272": "Packetalk LLC", + "154066463686656": "Beyond Laser Systems LLC", "154066463698944": "BRS Sistemas Eletrônicos", "154066463703040": "Tex Computer Srl", "154066463719424": "Hunan Oushi Electronic Technology Co.,Ltd", @@ -124264,6 +125056,7 @@ "154066464342016": "Scenario Automation", "154066464350208": "Pixus Technologies Inc.", "154066464358400": "Proserv", + "154066464362496": "Panascais ehf.", "154066464382976": "DAccess Security Systems P Ltd", "154066464399360": "Flextronics International Kft", "154066464411648": "LG-LHT Aircraft Solutions GmbH", @@ -124273,7 +125066,9 @@ "154066464460800": "RADA Electronics Industries Ltd.", "154066464473088": "VMukti Solutions Private Limited", "154066464485376": "Amiad Water Systems", + "154066464534528": "Teletech Services", "154066464542720": "Grossenbacher Systeme AG", + "154066464546816": "Shenzhen Forddok Technology Co., Ltd", "154066464550912": "Daedalean AG", "154066464559104": "Integer.pl S.A.", "154066464563200": "Nautel LTD", @@ -124306,6 +125101,7 @@ "154066464784384": "Ashinne Technology Co., Ltd", "154066464800768": "Power Electronics Espana, S.L.", "154066464825344": "ComVetia AG", + "154066464841728": "Changzhou MITO electronics Technology Co;LTD", "154066464845824": "Televic Rail GmbH", "154066464854016": "Plura", "154066464862208": "JieChuang HeYi(Beijing) Technology Co., Ltd.", @@ -124342,6 +125138,7 @@ "154066465280000": "Sigma Defense Systems LLC", "154066465292288": "Northwest Central Indiana Community Partnerships Inc dba Wabash Heartland Innovation Network (WHIN)", "154066465304576": "Warecube,Inc", + "154066465312768": "Novatera(Shenzhen)Technologies Co.,Ltd.", "154066465341440": "IoTSecure, LLC", "154066465345536": "Preston Industries dba PolyScience", "154066465357824": "Advantor Corporation", diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..d67c70cca0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,77 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socioeconomic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Common++/CMakeLists.txt b/Common++/CMakeLists.txt index 74910a1fd2..5b75bf9f77 100644 --- a/Common++/CMakeLists.txt +++ b/Common++/CMakeLists.txt @@ -2,6 +2,7 @@ add_library( Common++ src/GeneralUtils.cpp src/IpAddress.cpp + src/IpAddressUtils.cpp src/IpUtils.cpp src/Logger.cpp src/MacAddress.cpp @@ -11,8 +12,10 @@ add_library( src/TablePrinter.cpp) set(public_headers + header/DeprecationUtils.h header/GeneralUtils.h header/IpAddress.h + header/IpAddressUtils.h header/IpUtils.h header/Logger.h header/LRUList.h diff --git a/Common++/header/DeprecationUtils.h b/Common++/header/DeprecationUtils.h new file mode 100644 index 0000000000..afdc20cb18 --- /dev/null +++ b/Common++/header/DeprecationUtils.h @@ -0,0 +1,37 @@ +#pragma once + +/// @file + +#ifndef PCPP_DEPRECATED + #if defined(__GNUC__) || defined(__clang__) + #define PCPP_DEPRECATED(msg) __attribute__((deprecated(msg))) + #elif defined(_MSC_VER) + #define PCPP_DEPRECATED(msg) __declspec(deprecated(msg)) + #else + #pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") + #define PCPP_DEPRECATED(msg) + #endif +#endif + +#if !defined(DISABLE_WARNING_PUSH) || !defined(DISABLE_WARNING_POP) + #if defined(_MSC_VER) + #define DISABLE_WARNING_PUSH __pragma(warning( push )) + #define DISABLE_WARNING_POP __pragma(warning( pop )) + #define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber )) + + #define DISABLE_WARNING_DEPRECATED DISABLE_WARNING(4996) + #elif defined(__GNUC__) || defined(__clang__) + #define DO_PRAGMA(X) _Pragma(#X) + #define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) + #define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) + #define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName) + + #define DISABLE_WARNING_DEPRECATED DISABLE_WARNING(-Wdeprecated-declarations) + #else + #pragma message("WARNING: Disabling of warnings is not implemented for this compiler") + #define DISABLE_WARNING_PUSH + #define DISABLE_WARNING_POP + + #define DISABLE_WARNING_DEPRECATED + #endif +#endif diff --git a/Common++/header/GeneralUtils.h b/Common++/header/GeneralUtils.h index 198139008a..1f7e475b05 100644 --- a/Common++/header/GeneralUtils.h +++ b/Common++/header/GeneralUtils.h @@ -2,6 +2,7 @@ #include #include +#include /// @file @@ -64,4 +65,17 @@ namespace pcpp int mask = alignment - 1; return (number + mask) & ~mask; } + + /** + * A template class to calculate enum class hash + * @tparam EnumClass + */ + template::value , bool>::type = false> + struct EnumClassHash + { + size_t operator()(EnumClass value) const + { + return static_cast::type>(value); + } + }; } diff --git a/Common++/header/IpAddress.h b/Common++/header/IpAddress.h index 1a51d3ed50..52f83fb7c3 100644 --- a/Common++/header/IpAddress.h +++ b/Common++/header/IpAddress.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include /// @file @@ -39,41 +41,50 @@ namespace pcpp * A constructor that creates an instance of the class out of 4-byte integer value. * @param[in] addrAsInt The address as 4-byte integer in network byte order */ - IPv4Address(const uint32_t addrAsInt) { memcpy(m_Bytes, &addrAsInt, sizeof(m_Bytes)); } + IPv4Address(const uint32_t addrAsInt) { memcpy(m_Bytes.data(), &addrAsInt, sizeof(addrAsInt)); } /** * A constructor that creates an instance of the class out of 4-byte array. * @param[in] bytes The address as 4-byte array in network byte order */ - IPv4Address(const uint8_t bytes[4]) { memcpy(m_Bytes, bytes, sizeof(m_Bytes)); } + IPv4Address(const uint8_t bytes[4]) { memcpy(m_Bytes.data(), bytes, 4 * sizeof(uint8_t)); } + + /** + * A constructor that creates an instance of the class out of a 4-byte standard array. + * @param[in] bytes The address as 4-byte standard array in network byte order + */ + IPv4Address(const std::array& bytes) : m_Bytes(bytes) {} /** * A constructor that creates an instance of the class out of std::string value. - * If the string doesn't represent a valid IPv4 address, an exception is thrown. + * * @param[in] addrAsString The std::string representation of the address + * @throws std::invalid_argument The provided string does not represent a valid IPv4 address. */ IPv4Address(const std::string& addrAsString); /** - * Converts the IPv4 address into a 4B integer - * @return a 4B integer in network byte order representing the IPv4 address + * @return A 4-byte integer in network byte order representing the IPv4 address */ inline uint32_t toInt() const; /** - * Returns a pointer to 4-byte array representing the IPv4 address + * @return A non-owning pointer to 4-byte C-style array representing the IPv4 address */ - const uint8_t* toBytes() const { return m_Bytes; } + const uint8_t* toBytes() const { return m_Bytes.data(); } + + /** + * @return A reference to a 4-byte standard array representing the IPv4 address + */ + const std::array& toByteArray() const { return m_Bytes; } /** - * Returns a std::string representation of the address * @return A string representation of the address */ std::string toString() const; /** - * Determine whether the address is a multicast address - * @return True if an address is multicast + * @return True if an address is multicast, false otherwise. */ bool isMulticast() const; @@ -92,10 +103,10 @@ namespace pcpp bool operator<(const IPv4Address& rhs) const { uint32_t intVal = toInt(); - std::reverse((uint8_t*)(&intVal), (uint8_t*)(&intVal) + sizeof(intVal)); + std::reverse(reinterpret_cast(&intVal), reinterpret_cast(&intVal) + sizeof(intVal)); uint32_t rhsIntVal = rhs.toInt(); - std::reverse((uint8_t*)(&rhsIntVal), (uint8_t*)(&rhsIntVal) + sizeof(rhsIntVal)); + std::reverse(reinterpret_cast(&rhsIntVal), reinterpret_cast(&rhsIntVal) + sizeof(rhsIntVal)); return intVal < rhsIntVal; } @@ -149,7 +160,7 @@ namespace pcpp static const IPv4Address MulticastRangeUpperBound; private: - uint8_t m_Bytes[4] = {0}; + std::array m_Bytes = {0}; }; // class IPv4Address @@ -158,7 +169,7 @@ namespace pcpp uint32_t IPv4Address::toInt() const { uint32_t addr; - memcpy(&addr, m_Bytes, sizeof(m_Bytes)); + memcpy(&addr, m_Bytes.data(), m_Bytes.size() * sizeof(uint8_t)); return addr; } @@ -178,19 +189,33 @@ namespace pcpp * A constructor that creates an instance of the class out of 16-byte array. * @param[in] bytes The address as 16-byte array in network byte order */ - IPv6Address(const uint8_t bytes[16]) { memcpy(m_Bytes, bytes, sizeof(m_Bytes)); } + IPv6Address(const uint8_t bytes[16]) { memcpy(m_Bytes.data(), bytes, 16 * sizeof(uint8_t)); } + + /** + * A constructor that creates an instance of the class out of a 16-byte standard array. + * @param[in] bytes The address as 16-byte standard array in network byte order + */ + IPv6Address(const std::array& bytes) : m_Bytes(bytes) {} /** * A constructor that creates an instance of the class out of std::string value. - * If the string doesn't represent a valid IPv6 address, an exception is thrown. + * * @param[in] addrAsString The std::string representation of the address + * @throws std::invalid_argument The provided string does not represent a valid IPv6 address. */ IPv6Address(const std::string& addrAsString); /** - * Returns a pointer to 16-byte array representing the IPv6 address + * Returns a view of the IPv6 address as a 16-byte raw C-style array + * @return A non-owning pointer to 16-byte array representing the IPv6 address */ - const uint8_t* toBytes() const { return m_Bytes; } + const uint8_t* toBytes() const { return m_Bytes.data(); } + + /** + * Returns a view of the IPv6 address as a std::array of bytes + * @return A reference to a 16-byte standard array representing the IPv6 address + */ + const std::array& toByteArray() const { return m_Bytes; } /** * Returns a std::string representation of the address @@ -238,7 +263,7 @@ namespace pcpp * This method assumes array allocated size is at least 16 (the size of an IPv6 address) * @param[in] arr A pointer to the array which address will be copied to */ - void copyTo(uint8_t* arr) const { memcpy(arr, m_Bytes, sizeof(m_Bytes)); } + void copyTo(uint8_t* arr) const { memcpy(arr, m_Bytes.data(), m_Bytes.size() * sizeof(uint8_t)); } /** * Checks whether the address matches a network. @@ -282,7 +307,7 @@ namespace pcpp static const IPv6Address MulticastRangeLowerBound; private: - uint8_t m_Bytes[16] = {0}; + std::array m_Bytes = {0}; }; // class IPv6Address @@ -327,8 +352,9 @@ namespace pcpp /** * A constructor that creates an instance of the class out of std::string value - * If the string doesn't represent a valid IPv4 or IPv6 address, an instance will store an unspecified address + * * @param[in] addrAsString The std::string representation of the address + * @throws std::invalid_argument The provided string does not represent a valid IPv4 or IPv6 address. */ IPAddress(const std::string& addrAsString); @@ -462,13 +488,21 @@ namespace pcpp class IPv4Network { public: + /** + * A constructor that creates an instance of the class out of an address and a full prefix length, + * essentially making a network of consisting of only 1 address. + * + * @param address An address representing the network prefix. + */ + explicit IPv4Network(const IPv4Address& address) : IPv4Network(address, 32u) {} + /** * A constructor that creates an instance of the class out of an address representing the network prefix * and a prefix length * @param address An address representing the network prefix. If the address is invalid std::invalid_argument * exception is thrown - * @param prefixLen A number between 0 and 32 representing the prefix length. If another value is provided - * std::invalid_argument exception is thrown + * @param prefixLen A number between 0 and 32 representing the prefix length. + * @throws std::invalid_argument Prefix length is out of acceptable range. */ IPv4Network(const IPv4Address& address, uint8_t prefixLen); @@ -479,8 +513,8 @@ namespace pcpp * exception is thrown * @param netmask A string representing a netmask in the format of X.X.X.X, for example: 255.255.0.0. * Please notice that netmasks that start with zeros are invalid, for example: 0.0.255.255. The only netmask - * starting with zeros that is valid is 0.0.0.0. If the netmask is invalid std::invalid_argument - * exception is thrown + * starting with zeros that is valid is 0.0.0.0. + * @throws std::invalid_argument The provided netmask is invalid. */ IPv4Network(const IPv4Address& address, const std::string& netmask); @@ -492,7 +526,7 @@ namespace pcpp * 0 and 32 representing the network prefix * - X.X.X.X/Y.Y.Y.Y where X.X.X.X is a valid IPv4 address representing the network prefix and Y.Y.Y.Y is * a valid netmask - * For any invalid value std::invalid_argument is thrown + * @throws std::invalid_argument The provided string does not represent a valid address and netmask format. */ IPv4Network(const std::string& addressAndNetmask); @@ -565,13 +599,21 @@ namespace pcpp class IPv6Network { public: + /** + * A constructor that creates an instance of the class out of an address and a full prefix length, + * essentially making a network of consisting of only 1 address. + * + * @param address An address representing the network prefix. + */ + explicit IPv6Network(const IPv6Address& address) : IPv6Network(address, 128u) {} + /** * A constructor that creates an instance of the class out of an address representing the network prefix * and a prefix length * @param address An address representing the network prefix. If the address is invalid std::invalid_argument * exception is thrown - * @param prefixLen A number between 0 and 128 representing the prefix length. If another value is provided - * std::invalid_argument exception is thrown + * @param prefixLen A number between 0 and 128 representing the prefix length. + * @throws std::invalid_argument Prefix length is out of acceptable range. */ IPv6Network(const IPv6Address& address, uint8_t prefixLen); @@ -582,8 +624,8 @@ namespace pcpp * exception is thrown * @param netmask A string representing a netmask in valid IPv6 format, for example: ffff:ffff::. * Please notice that netmasks that start with zeros are invalid, for example: 0:ffff::. The only netmask - * starting with zeros that is valid is all zeros (::). If the netmask is invalid std::invalid_argument - * exception is thrown + * starting with zeros that is valid is all zeros (::). + * @throws std::invalid_argument The provided netmask is invalid. */ IPv6Network(const IPv6Address& address, const std::string& netmask); @@ -595,7 +637,7 @@ namespace pcpp * a number between 0 and 128 representing the network prefix * - IPV6_ADDRESS/IPV6_NETMASK where IPV6_ADDRESS is a valid IPv6 address representing the network prefix * and IPV6_NETMASK is a valid IPv6 netmask - * For any invalid value std::invalid_argument is thrown + * @throws std::invalid_argument The provided string does not represent a valid address and netmask format. */ IPv6Network(const std::string& addressAndNetmask); @@ -668,25 +710,31 @@ namespace pcpp class IPNetwork { public: + /** + * A constructor that creates an instance of the class out of an IP address and a full prefix length, + * essentially making a network of consisting of only 1 address. + * + * @param address An address representing the network prefix. + */ + explicit IPNetwork(const IPAddress& address) : IPNetwork(address, address.isIPv4() ? 32u : 128u) {} + /** * A constructor that creates an instance of the class out of an address representing the network prefix * and a prefix length * @param address An address representing the network prefix. If the address is invalid std::invalid_argument * exception is thrown - * @param prefixLen A number representing the prefix length. If the value isn't in the range allowed for the - * network (0 - 32 for IPv4 networks or 0 - 128 for IPv6 networks) and std::invalid_argument exception is thrown + * @param prefixLen A number representing the prefix length. Allowed ranges are 0 - 32 for IPv4 networks and 0 - 128 for IPv6 networks. + * @throws std::invalid_argument Prefix length is out of acceptable range. */ IPNetwork(const IPAddress& address, uint8_t prefixLen) { if (address.isIPv4()) { - m_IPv4Network = new IPv4Network(address.getIPv4(), prefixLen); - m_IPv6Network = nullptr; + m_IPv4Network = std::unique_ptr(new IPv4Network(address.getIPv4(), prefixLen)); } else { - m_IPv6Network = new IPv6Network(address.getIPv6(), prefixLen); - m_IPv4Network = nullptr; + m_IPv6Network = std::unique_ptr(new IPv6Network(address.getIPv6(), prefixLen)); } } @@ -699,19 +747,17 @@ namespace pcpp * or 255.255.0.0 for IPv4 networks. * Please notice that netmasks that start with zeros are invalid, for example: 0:ffff:: or 0.255.255.255. * The only netmask starting with zeros that is valid is all zeros (:: or 0.0.0.0). - * If the netmask is invalid std::invalid_argument exception is thrown + * @throws std::invalid_argument The provided netmask is invalid. */ IPNetwork(const IPAddress& address, const std::string& netmask) { if (address.isIPv4()) { - m_IPv4Network = new IPv4Network(address.getIPv4(), netmask); - m_IPv6Network = nullptr; + m_IPv4Network = std::unique_ptr(new IPv4Network(address.getIPv4(), netmask)); } else { - m_IPv6Network = new IPv6Network(address.getIPv6(), netmask); - m_IPv4Network = nullptr; + m_IPv6Network = std::unique_ptr(new IPv6Network(address.getIPv6(), netmask)); } } @@ -723,19 +769,17 @@ namespace pcpp * a number representing the network prefix * - IP_ADDRESS/NETMASK where IP_ADDRESS is a valid IP address representing the network prefix and NETMASK * is a valid netmask for this type of network (IPv4 or IPv6 network) - * For any invalid value std::invalid_argument is thrown + * @throws std::invalid_argument The provided string does not represent a valid address and netmask format. */ IPNetwork(const std::string& addressAndNetmask) { try { - m_IPv4Network = new IPv4Network(addressAndNetmask); - m_IPv6Network = nullptr; + m_IPv4Network = std::unique_ptr(new IPv4Network(addressAndNetmask)); } catch (const std::invalid_argument&) { - m_IPv6Network = new IPv6Network(addressAndNetmask); - m_IPv4Network = nullptr; + m_IPv6Network = std::unique_ptr(new IPv6Network(addressAndNetmask)); } } @@ -745,33 +789,14 @@ namespace pcpp */ IPNetwork(const IPNetwork& other) { - m_IPv4Network = nullptr; - m_IPv6Network = nullptr; - if (other.m_IPv4Network) { - m_IPv4Network = new IPv4Network(*other.m_IPv4Network); + m_IPv4Network = std::unique_ptr(new IPv4Network(*other.m_IPv4Network)); } if (other.m_IPv6Network) { - m_IPv6Network = new IPv6Network(*other.m_IPv6Network); - } - } - - /** - * A destructor for this class - */ - ~IPNetwork() - { - if (m_IPv4Network) - { - delete m_IPv4Network; - } - - if (m_IPv6Network) - { - delete m_IPv6Network; + m_IPv6Network = std::unique_ptr(new IPv6Network(*other.m_IPv6Network)); } } @@ -801,17 +826,15 @@ namespace pcpp { if (m_IPv4Network) { - delete m_IPv4Network; m_IPv4Network = nullptr; } if (m_IPv6Network) { - delete m_IPv6Network; m_IPv6Network = nullptr; } - m_IPv4Network = new IPv4Network(other); + m_IPv4Network = std::unique_ptr(new IPv4Network(other)); return *this; } @@ -825,17 +848,15 @@ namespace pcpp { if (m_IPv4Network) { - delete m_IPv4Network; m_IPv4Network = nullptr; } if (m_IPv6Network) { - delete m_IPv6Network; m_IPv6Network = nullptr; } - m_IPv6Network = new IPv6Network(other); + m_IPv6Network = std::unique_ptr(new IPv6Network(other)); return *this; } @@ -850,8 +871,8 @@ namespace pcpp } /** - * @return The netmask, for example: the netmask of 3546::/16 is ffff::, the netmask of 10.10.10.10/8 is 255.0.0.0 - */ + * @return The netmask, for example: the netmask of 3546::/16 is ffff::, the netmask of 10.10.10.10/8 is 255.0.0.0 + */ std::string getNetmask() const { return (m_IPv4Network != nullptr ? m_IPv4Network->getNetmask() : m_IPv6Network->getNetmask()); @@ -972,8 +993,8 @@ namespace pcpp } private: - IPv4Network* m_IPv4Network; - IPv6Network* m_IPv6Network; + std::unique_ptr m_IPv4Network; + std::unique_ptr m_IPv6Network; }; } // namespace pcpp diff --git a/Common++/header/IpAddressUtils.h b/Common++/header/IpAddressUtils.h new file mode 100644 index 0000000000..f561a634a7 --- /dev/null +++ b/Common++/header/IpAddressUtils.h @@ -0,0 +1,103 @@ +#pragma once + +/// @file + +// Forward declarations +struct in_addr; +struct in6_addr; + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + // Forward declarations + class IPv4Address; + class IPv6Address; + class IPAddress; + + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPv4Address& lhs, const in_addr& rhs); + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const IPv4Address& lhs, const in_addr& rhs) { return !(lhs == rhs); } + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + inline bool operator==(const in_addr& lhs, const IPv4Address& rhs) { return rhs == lhs; } + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const in_addr& lhs, const IPv4Address& rhs) { return !(lhs == rhs); } + + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPv6Address& lhs, const in6_addr& rhs); + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const IPv6Address& lhs, const in6_addr& rhs) { return !(lhs == rhs); } + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + inline bool operator==(const in6_addr& lhs, const IPv6Address& rhs) { return rhs == lhs; } + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const in6_addr& lhs, const IPv6Address& rhs) { return !(lhs == rhs); } + + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPAddress& lhs, const in_addr& rhs); + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const IPAddress& lhs, const in_addr& rhs) { return !(lhs == rhs); } + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + inline bool operator==(const in_addr& lhs, const IPAddress& rhs) { return rhs == lhs; } + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const in_addr& lhs, const IPAddress& rhs) { return !(lhs == rhs); } + + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + bool operator==(const IPAddress& lhs, const in6_addr& rhs); + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const IPAddress& lhs, const in6_addr& rhs) { return !(lhs == rhs); } + /** + * Overload of the equal-to operator + * @return True if the addresses are equal, false otherwise + */ + inline bool operator==(const in6_addr& lhs, const IPAddress& rhs) { return rhs == lhs; } + /** + * Overload of the not-equal-to operator + * @return True if the addresses differ, false otherwise + */ + inline bool operator!=(const in6_addr& lhs, const IPAddress& rhs) { return !(lhs == rhs); } +} diff --git a/Common++/header/IpUtils.h b/Common++/header/IpUtils.h index cd35ed58c9..6ad4d83146 100644 --- a/Common++/header/IpUtils.h +++ b/Common++/header/IpUtils.h @@ -62,22 +62,40 @@ namespace pcpp * Extract IPv4 address from sockaddr * @param[in] sa - input sockaddr * @return Address in in_addr format + * @throws std::invalid_argument Sockaddr family is not AF_INET or sockaddr is nullptr. */ - in_addr* sockaddr2in_addr(struct sockaddr *sa); + in_addr* sockaddr2in_addr(sockaddr* sa); + + /** + * Attempt to extract IPv4 address from sockaddr + * @param[in] sa - input sockaddr + * @return Pointer to address in in_addr format or nullptr if extraction fails. + */ + in_addr* try_sockaddr2in_addr(sockaddr* sa); /** * Extract IPv6 address from sockaddr * @param[in] sa - input sockaddr * @return Address in in6_addr format + * @throws std::invalid_argument Sockaddr family is not AF_INET6 or sockaddr is nullptr. + */ + in6_addr* sockaddr2in6_addr(sockaddr* sa); + + /** + * Attempt to extract IPv6 address from sockaddr + * @param[in] sa - input sockaddr + * @return Pointer to address in in6_addr format or nullptr if extraction fails. */ - in6_addr* sockaddr2in6_addr(struct sockaddr *sa); + in6_addr* try_sockaddr2in6_addr(sockaddr* sa); /** * Converts a sockaddr format address to its string representation * @param[in] sa Address in sockaddr format - * @param[out] resultString String representation of the address + * @param[out] resultString String representation of the address + * @param[in] resultBufLen Length of the result buffer. + * @throws std::invalid_argument Sockaddr family is not AF_INET or AF_INET6, sockaddr is nullptr or the result str buffer is insufficient. */ - void sockaddr2string(struct sockaddr *sa, char* resultString); + void sockaddr2string(sockaddr const* sa, char* resultString, size_t resultBufLen); /** * Convert a in_addr format address to 32bit representation diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index a32c344205..ad73716274 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -58,6 +58,7 @@ namespace pcpp PacketLogModuleRawPacket, ///< RawPacket module (Packet++) PacketLogModulePacket, ///< Packet module (Packet++) PacketLogModuleLayer, ///< Layer module (Packet++) + PacketLogModuleAsn1Codec, ///< Asn1Codec module (Packet++) PacketLogModuleArpLayer, ///< ArpLayer module (Packet++) PacketLogModuleEthLayer, ///< EthLayer module (Packet++) PacketLogModuleIPv4Layer, ///< IPv4Layer module (Packet++) @@ -93,7 +94,6 @@ namespace pcpp PacketLogModuleTelnetLayer, ///< TelnetLayer module (Packet++) PacketLogModuleStpLayer, ///< StpLayer module (Packet++) PacketLogModuleLLCLayer, ///< LLCLayer module (Packet++) - PacketLogModuleSingleCommandTextProtocolLayer, ///< SingleCommandTextProtocol module (Packet++) PacketLogModuleNdpLayer, ///< NdpLayer module (Packet++) PacketLogModuleFtpLayer, ///< FtpLayer module (Packet++) PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++) diff --git a/Common++/header/PointerVector.h b/Common++/header/PointerVector.h index 50014390af..d73d60238f 100644 --- a/Common++/header/PointerVector.h +++ b/Common++/header/PointerVector.h @@ -138,6 +138,11 @@ namespace pcpp */ T* front() { return m_Vector.front(); } + /** + * @return A pointer to the last element in the vector + */ + T* back() { return m_Vector.back(); } + /** * Removes from the vector a single element (position). Once the element is erased, it's also freed * @param[in] position The position of the element to erase diff --git a/Common++/header/SystemUtils.h b/Common++/header/SystemUtils.h index d63ba20733..ad03d1dec8 100644 --- a/Common++/header/SystemUtils.h +++ b/Common++/header/SystemUtils.h @@ -228,8 +228,9 @@ namespace pcpp * Execute a shell command and return its output * @param[in] command The command to run * @return The output of the command (both stdout and stderr) + * @throws std::runtime_error Error executing the command. */ - std::string executeShellCommand(const std::string &command); + std::string executeShellCommand(const std::string& command); /** * Check if a directory exists diff --git a/Common++/src/IpAddress.cpp b/Common++/src/IpAddress.cpp index 84b9103b7f..ac4ffb8afa 100644 --- a/Common++/src/IpAddress.cpp +++ b/Common++/src/IpAddress.cpp @@ -51,7 +51,7 @@ namespace pcpp IPv4Address::IPv4Address(const std::string& addrAsString) { - if (inet_pton(AF_INET, addrAsString.data(), m_Bytes) <= 0) + if (inet_pton(AF_INET, addrAsString.data(), m_Bytes.data()) <= 0) { throw std::invalid_argument("Not a valid IPv4 address: " + addrAsString); } @@ -109,7 +109,7 @@ namespace pcpp IPv6Address::IPv6Address(const std::string& addrAsString) { - if(inet_pton(AF_INET6, addrAsString.data(), m_Bytes) <= 0) + if(inet_pton(AF_INET6, addrAsString.data(), m_Bytes.data()) <= 0) { throw std::invalid_argument("Not a valid IPv6 address: " + addrAsString); } @@ -118,10 +118,10 @@ namespace pcpp void IPv6Address::copyTo(uint8_t** arr, size_t& length) const { - const size_t addrLen = sizeof(m_Bytes); + const size_t addrLen = m_Bytes.size() * sizeof(uint8_t); length = addrLen; *arr = new uint8_t[addrLen]; - memcpy(*arr, m_Bytes, addrLen); + memcpy(*arr, m_Bytes.data(), addrLen); } @@ -237,11 +237,11 @@ namespace pcpp } catch(const std::exception&) { - throw std::invalid_argument("Netmask is not valid: " + netmask); + throw std::invalid_argument("Netmask is not valid IPv4 format: " + netmask); } if (!isValidNetmask(netmaskAddr)) { - throw std::invalid_argument("Netmask is not valid: " + netmask); + throw std::invalid_argument("Netmask is not valid IPv4 format: " + netmask); } initFromAddressAndNetmask(address, netmaskAddr); } @@ -288,11 +288,11 @@ namespace pcpp } catch (const std::invalid_argument&) { - throw std::invalid_argument("Netmask is not valid: " + netmaskStr); + throw std::invalid_argument("Netmask is not valid IPv4 format: " + netmaskStr); } if (!isValidNetmask(netmaskAddr)) { - throw std::invalid_argument("Netmask is not valid: " + netmaskStr); + throw std::invalid_argument("Netmask is not valid IPv4 format: " + netmaskStr); } initFromAddressAndNetmask(networkPrefix, netmaskAddr); } @@ -456,11 +456,11 @@ namespace pcpp } catch(const std::exception&) { - throw std::invalid_argument("Netmask is not valid: " + netmask); + throw std::invalid_argument("Netmask is not valid IPv6 format: " + netmask); } if (!isValidNetmask(netmaskAddr)) { - throw std::invalid_argument("Netmask is not valid: " + netmask); + throw std::invalid_argument("Netmask is not valid IPv6 format: " + netmask); } initFromAddressAndNetmask(address, netmaskAddr); } @@ -505,11 +505,11 @@ namespace pcpp } catch(const std::exception&) { - throw std::invalid_argument("Netmask is not valid: " + netmaskStr); + throw std::invalid_argument("Netmask is not valid IPv6 format: " + netmaskStr); } if (!isValidNetmask(netmaskAddr)) { - throw std::invalid_argument("Netmask is not valid: " + netmaskStr); + throw std::invalid_argument("Netmask is not valid IPv6 format: " + netmaskStr); } initFromAddressAndNetmask(networkPrefix, netmaskAddr); } diff --git a/Common++/src/IpAddressUtils.cpp b/Common++/src/IpAddressUtils.cpp new file mode 100644 index 0000000000..c72c944cb5 --- /dev/null +++ b/Common++/src/IpAddressUtils.cpp @@ -0,0 +1,35 @@ +#include "IpAddressUtils.h" + +#include "IpAddress.h" +#include "IpUtils.h" // Just needing in_addr and in6_addr. + +namespace pcpp +{ + bool operator==(const IPv4Address& lhs, const in_addr& rhs) + { + return lhs.toInt() == rhs.s_addr; + } + + bool operator==(const IPv6Address& lhs, const in6_addr& rhs) + { + return memcmp(lhs.toBytes(), &rhs, sizeof(struct in6_addr)) == 0; + } + + bool operator==(const IPAddress& lhs, const in_addr& rhs) + { + if (lhs.isIPv4()) + { + return lhs.getIPv4() == rhs; + } + return false; + } + + bool operator==(const IPAddress& lhs, const in6_addr& rhs) + { + if (lhs.isIPv6()) + { + return lhs.getIPv6() == rhs; + } + return false; + } +} diff --git a/Common++/src/IpUtils.cpp b/Common++/src/IpUtils.cpp index c78a1807f5..04337eae30 100644 --- a/Common++/src/IpUtils.cpp +++ b/Common++/src/IpUtils.cpp @@ -4,6 +4,7 @@ #include "Logger.h" #include #include +#include #ifndef NS_INADDRSZ #define NS_INADDRSZ 4 #endif @@ -18,36 +19,87 @@ namespace pcpp { namespace internal { - in_addr* sockaddr2in_addr(struct sockaddr* sa) + in_addr* sockaddr2in_addr(sockaddr* sa) { if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + if (sa->sa_family != AF_INET) + throw std::invalid_argument("sockaddr family is not AF_INET."); + + return &(reinterpret_cast(sa)->sin_addr); + } + + in_addr* try_sockaddr2in_addr(sockaddr* sa) + { + try + { + return sockaddr2in_addr(sa); + } + catch (const std::invalid_argument& e) + { + PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); return nullptr; - if (sa->sa_family == AF_INET) - return &(((struct sockaddr_in*)sa)->sin_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); - return nullptr; + } } - in6_addr* sockaddr2in6_addr(struct sockaddr* sa) + in6_addr* sockaddr2in6_addr(sockaddr* sa) { - if (sa->sa_family == AF_INET6) - return &(((struct sockaddr_in6*)sa)->sin6_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); - return nullptr; + if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + if (sa->sa_family != AF_INET6) + throw std::invalid_argument("sockaddr family is not AF_INET6."); + + return &(reinterpret_cast(sa)->sin6_addr); } - void sockaddr2string(struct sockaddr* sa, char* resultString) + in6_addr* try_sockaddr2in6_addr(sockaddr* sa) { - in_addr* ipv4Addr = sockaddr2in_addr(sa); - if (ipv4Addr != nullptr) + try + { + return sockaddr2in6_addr(sa); + } + catch (const std::invalid_argument& e) + { + PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); + return nullptr; + } + } + + void sockaddr2string(sockaddr const* sa, char* resultString, size_t resultBufLen) + { + if (sa == nullptr) + throw std::invalid_argument("sockaddr is nullptr"); + + switch (sa->sa_family) + { + case AF_INET: { PCPP_LOG_DEBUG("IPv4 packet address"); - inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, INET_ADDRSTRLEN); + if (resultBufLen < INET_ADDRSTRLEN) + throw std::invalid_argument("Insufficient buffer"); + + if (inet_ntop(AF_INET, &(reinterpret_cast(sa)->sin_addr), resultString, resultBufLen) == nullptr) + { + throw std::runtime_error("Unknown error during conversion"); + } + break; } - else + case AF_INET6: { - PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); - inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, INET6_ADDRSTRLEN); + PCPP_LOG_DEBUG("IPv6 packet address"); + if (resultBufLen < INET6_ADDRSTRLEN) + throw std::invalid_argument("Insufficient buffer"); + + if(inet_ntop(AF_INET6, &(reinterpret_cast(sa)->sin6_addr), resultString, resultBufLen) == nullptr) + { + throw std::runtime_error("Unknown error during conversion"); + } + break; + } + default: + throw std::invalid_argument("Unsupported sockaddr family. Family is not AF_INET or AF_INET6."); } } diff --git a/Common++/src/SystemUtils.cpp b/Common++/src/SystemUtils.cpp index 0bc9fdf0d0..85dbbc0d37 100644 --- a/Common++/src/SystemUtils.cpp +++ b/Common++/src/SystemUtils.cpp @@ -4,6 +4,9 @@ #ifndef _MSC_VER #include #endif +#include +#include +#include #include #include #include @@ -48,6 +51,22 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp) } #endif +/// @cond PCPP_INTERNAL + +namespace +{ + /** + * @class PcloseDeleter + * A deleter that cleans up a FILE handle using pclose. + */ + struct PcloseDeleter + { + void operator()(FILE* ptr) const { PCLOSE(ptr); } + }; +} // namespace + +/// @endcond + namespace pcpp { @@ -183,18 +202,21 @@ void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& re } } -std::string executeShellCommand(const std::string &command) +std::string executeShellCommand(const std::string& command) { - FILE* pipe = POPEN(command.c_str(), "r"); - if (!pipe) return "ERROR"; - char buffer[128]; - std::string result = ""; - while(!feof(pipe)) + std::unique_ptr pipe = std::unique_ptr(POPEN(command.c_str(), "r")); + if (!pipe) + { + throw std::runtime_error("Error executing command: " + command); + } + + std::array buffer; + std::string result; + while(!feof(pipe.get())) { - if(fgets(buffer, 128, pipe) != nullptr) - result += buffer; + if(fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) + result += buffer.data(); // Using the C-string overload of string append. } - PCLOSE(pipe); return result; } diff --git a/Examples/KniPong/main.cpp b/Examples/KniPong/main.cpp index 35d0c05fa1..c03bb187d0 100644 --- a/Examples/KniPong/main.cpp +++ b/Examples/KniPong/main.cpp @@ -241,8 +241,15 @@ inline bool setKniIp(const pcpp::IPv4Address& ip, const std::string& kniName) pcpp::executeShellCommand(command.str()); command.str(""); command << "ip a | grep " << ip; - std::string result = pcpp::executeShellCommand(command.str()); - return result != "" && result != "ERROR"; + try + { + std::string result = pcpp::executeShellCommand(command.str()); + return result != ""; + } + catch (const std::runtime_error&) + { + return false; + } } diff --git a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp index 3e6a2ce23b..71823e5748 100644 --- a/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketCraftAndEdit/main.cpp @@ -70,7 +70,7 @@ int main(int argc, char* argv[]) // add URG flag tcpLayer->getTcpHeader()->urgFlag = 1; // add MSS TCP option - tcpLayer->addTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TCPOPT_MSS, (uint16_t)1460)); + tcpLayer->insertTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TcpOptionEnumType::Mss, (uint16_t)1460)); // let's get the HTTP layer auto* httpRequestLayer = parsedPacket.getLayerOfType(); diff --git a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp index b1d81a8898..15999f00c4 100644 --- a/Examples/Tutorials/Tutorial-PacketParsing/main.cpp +++ b/Examples/Tutorials/Tutorial-PacketParsing/main.cpp @@ -42,13 +42,13 @@ std::string printTcpFlags(pcpp::TcpLayer* tcpLayer) return result; } -std::string printTcpOptionType(pcpp::TcpOptionType optionType) +std::string printTcpOptionType(pcpp::TcpOptionEnumType optionType) { switch (optionType) { - case pcpp::PCPP_TCPOPT_NOP: + case pcpp::TcpOptionEnumType::Nop: return "NOP"; - case pcpp::PCPP_TCPOPT_TIMESTAMP: + case pcpp::TcpOptionEnumType::Timestamp: return "Timestamp"; default: return "Other"; @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) std::cout << "TCP options: "; for (pcpp::TcpOption tcpOption = tcpLayer->getFirstTcpOption(); tcpOption.isNotNull(); tcpOption = tcpLayer->getNextTcpOption(tcpOption)) { - std::cout << printTcpOptionType(tcpOption.getTcpOptionType()) << " "; + std::cout << printTcpOptionType(tcpOption.getTcpOptionEnumType()) << " "; } std::cout << std::endl; diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index d0a05c4046..cc7a8268d2 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -2,6 +2,7 @@ add_library( Packet++ $ src/ArpLayer.cpp + src/Asn1Codec.cpp src/BgpLayer.cpp src/CotpLayer.cpp src/DhcpLayer.cpp @@ -24,6 +25,7 @@ add_library( src/IPv6Extensions.cpp src/IPv6Layer.cpp src/Layer.cpp + src/LdapLayer.cpp src/LLCLayer.cpp src/MplsLayer.cpp src/NdpLayer.cpp @@ -67,6 +69,7 @@ add_library( set(public_headers header/ArpLayer.h + header/Asn1Codec.h header/BgpLayer.h header/CotpLayer.h header/DhcpLayer.h @@ -91,6 +94,7 @@ set(public_headers header/IPv6Extensions.h header/IPv6Layer.h header/Layer.h + header/LdapLayer.h header/LLCLayer.h header/MplsLayer.h header/NullLoopbackLayer.h diff --git a/Packet++/header/Asn1Codec.h b/Packet++/header/Asn1Codec.h new file mode 100644 index 0000000000..7eeb48a526 --- /dev/null +++ b/Packet++/header/Asn1Codec.h @@ -0,0 +1,540 @@ +#pragma once + +#include +#include +#include +#include +#include "PointerVector.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + /** + * An enum for representing ASN.1 tag class + */ + enum class Asn1TagClass : uint8_t + { + /** The Universal tag class */ + Universal = 0, + /** The Application tag class */ + Application = 1, + /** The Context-Specific tag class */ + ContextSpecific = 2, + /** The Private tag class */ + Private = 3, + }; + + /** + * An enum for representing ASN.1 Universal tag types + */ + enum class Asn1UniversalTagType : uint8_t + { + /** The reserved identifier for the End-of-Contents marker in an indefinite length encoding */ + EndOfContent = 0, + /** The universal tag type for Boolean */ + Boolean = 1, + /** The universal tag type for Integer */ + Integer = 2, + /** The universal tag type for Bit String */ + BitString = 3, + /** The universal tag type for Octet String */ + OctetString = 4, + /** The universal tag type for Null */ + Null = 5, + /** The universal tag type for Object Identifier */ + ObjectIdentifier = 6, + /** The universal tag type for Object Descriptor */ + ObjectDescriptor = 7, + /** The universal tag type for External */ + External = 8, + /** The universal tag type for Real */ + Real = 9, + /** The universal tag type for Enumerated */ + Enumerated = 10, + /** The universal tag type for Embedded-PDV */ + EmbeddedPDV = 11, + /** The universal tag type for UTF8 String */ + UTF8String = 12, + /** The universal tag type for Relative Object Identifier */ + RelativeObjectIdentifier = 13, + /** The universal tag type for Time */ + Time = 14, + /** A reserved value */ + Reserved = 15, + /** The universal tag type Sequence */ + Sequence = 16, + /** The universal tag type for Set */ + Set = 17, + /** The universal tag type for Numeric String */ + NumericString = 18, + /** The universal tag type for Printable String */ + PrintableString = 19, + /** The universal tag type for T61String */ + T61String = 20, + /** The universal tag type for Videotex String */ + VideotexString = 21, + /** The universal tag type for IA5String */ + IA5String = 22, + /** The universal tag type for UTC time */ + UTCTime = 23, + /** The universal tag type for Generalized time */ + GeneralizedTime = 24, + /** The universal tag type for GraphicString */ + GraphicString = 25, + /** The universal tag type for VisibleString */ + VisibleString = 26, + /** The universal tag type for GeneralString */ + GeneralString = 27, + /** The universal tag type for UniversalString */ + UniversalString = 28, + /** The universal tag type for CharacterString */ + CharacterString = 29, + /** The universal tag type for BMPString */ + BMPString = 30, + /** The universal tag type for Date */ + Date = 31, + /** The universal tag type for Time of Day */ + TimeOfDay = 32, + /** The universal tag type for Date-Time */ + DateTime = 33, + /** The universal tag type for Duration */ + Duration = 34, + /** The universal tag type for Object Identifier Internationalized Resource Identifier (IRI) */ + ObjectIdentifierIRI = 35, + /** The universal tag type for Relative Object Identifier Internationalized Resource Identifier (IRI) */ + RelativeObjectIdentifierIRI = 36, + /** A non-applicable value */ + NotApplicable = 255 + }; + + /** + * @class Asn1Record + * Represents an ASN.1 record, as described in ITU-T Recommendation X.680: + * + * + * + */ + class Asn1Record + { + public: + /** + * A static method to decode a byte array into an Asn1Record + * @param data A byte array to decode + * @param dataLen The byte array length + * @param lazy Use lazy decoding, set to true by default. Lazy decoding entails delaying the decoding + * of the record value until it is accessed + * @return A smart pointer to the decoded ASN.1 record. If the byte stream is not a valid ASN.1 record + * an exception is thrown + */ + static std::unique_ptr decode(const uint8_t* data, size_t dataLen, bool lazy = true); + + /** + * Encode this record and convert it to a byte stream + * @return A vector of bytes representing the record + */ + std::vector encode(); + + /** + * @return The ASN.1 tag class + */ + Asn1TagClass getTagClass() const { return m_TagClass; } + + /** + * @return True if it's a constructed record, or false if it's a primitive record + */ + bool isConstructed() const { return m_IsConstructed; } + + /** + * @return The ASN.1 Universal tag type if the record is of class Universal, otherwise Asn1UniversalTagType#NotApplicable + */ + Asn1UniversalTagType getUniversalTagType() const; + + /** + * @return The ASN.1 tag type value + */ + uint8_t getTagType() const { return m_TagType; } + + /** + * @return The length of the record value + */ + size_t getValueLength() const { return m_ValueLength; } + + /** + * @return The total length of the record + */ + size_t getTotalLength() const { return m_TotalLength; } + + /** + * @return A string representation of the record + */ + std::string toString(); + + /** + * A templated method that accepts a class derived from Asn1Record as its template argument and attempts + * to cast the current instance to that type + * @tparam Asn1RecordType The type to cast to + * @return A pointer to the type after casting + */ + template + Asn1RecordType* castAs() + { + auto result = dynamic_cast(this); + if (result == nullptr) + { + throw std::bad_cast(); + } + return result; + } + + virtual ~Asn1Record() = default; + + protected: + Asn1TagClass m_TagClass = Asn1TagClass::Universal; + bool m_IsConstructed = false; + uint8_t m_TagType = 0; + + size_t m_ValueLength = 0; + size_t m_TotalLength = 0; + + uint8_t* m_EncodedValue = nullptr; + + Asn1Record() = default; + + static Asn1Record* decodeInternal(const uint8_t* data, size_t dataLen, bool lazy); + + virtual void decodeValue(uint8_t* data, bool lazy) = 0; + virtual std::vector encodeValue() const = 0; + + static Asn1Record* decodeTagAndCreateRecord(const uint8_t* data, size_t dataLen, int& tagLen); + int decodeLength(const uint8_t* data, size_t dataLen); + void decodeValueIfNeeded(); + + uint8_t encodeTag(); + std::vector encodeLength() const; + + virtual std::vector toStringList(); + + friend class Asn1ConstructedRecord; + }; + + /** + * @class Asn1GenericRecord + * Represents a generic ASN.1 record, either of an unknown type or of a known type that doesn't + * have a dedicated parser yet + */ + class Asn1GenericRecord : public Asn1Record + { + friend class Asn1Record; + + public: + /** + * A constructor to create a generic record + * @param tagClass The record tag class + * @param isConstructed A flag to indicate if the record is constructed or primitive + * @param tagType The record tag type value + * @param value A byte array of the tag value + * @param valueLen The length of the value byte array + */ + Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value, size_t valueLen); + + /** + * A constructor to create a generic record + * @param tagClass The record tag class + * @param isConstructed A flag to indicate if the record is constructed or primitive + * @param tagType The record tag type value + * @param value A string representing the tag value + */ + Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const std::string& value); + + ~Asn1GenericRecord() override; + + /** + * @return A pointer to the tag value + */ + const uint8_t* getValue() { decodeValueIfNeeded(); return m_Value; } + + protected: + Asn1GenericRecord() = default; + + void decodeValue(uint8_t* data, bool lazy) override; + std::vector encodeValue() const override; + + private: + uint8_t* m_Value = nullptr; + + void init(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value, size_t valueLen); + }; + + /** + * @class Asn1ConstructedRecord + * Represents a constructed ASN.1 record, which is a record that has sub-records + */ + class Asn1ConstructedRecord : public Asn1Record + { + friend class Asn1Record; + + public: + /** + * A constructor to create a constructed record + * @param tagClass The record tag class + * @param tagType The record tag type value + * @param subRecords A list of sub-records to assign as the record value + */ + explicit Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType, const std::vector& subRecords); + + /** + * A constructor to create a constructed record + * @param tagClass The record tag class + * @param tagType The record tag type value + * @param subRecords A PointerVector of sub-records to assign as the record value + */ + explicit Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType, const PointerVector& subRecords); + + /** + * @return A reference to the list of sub-records. It's important to note that any modifications made to + * this list will directly affect the internal structure + */ + PointerVector& getSubRecords() { decodeValueIfNeeded(); return m_SubRecords; }; + + protected: + Asn1ConstructedRecord() = default; + + void decodeValue(uint8_t* data, bool lazy) override; + std::vector encodeValue() const override; + + std::vector toStringList() override; + + template + void init(Asn1TagClass tagClass, uint8_t tagType, Iterator begin, Iterator end) + { + m_TagType = tagType; + m_TagClass = tagClass; + m_IsConstructed = true; + + size_t recordValueLength = 0; + for (Iterator recordIter = begin; recordIter != end; ++recordIter) + { + auto encodedRecord = (*recordIter)->encode(); + auto copyRecord = Asn1Record::decode(encodedRecord.data(), encodedRecord.size(), false); + m_SubRecords.pushBack(copyRecord.release()); + recordValueLength += encodedRecord.size(); + } + + m_ValueLength = recordValueLength; + m_TotalLength = recordValueLength + 1 + (m_ValueLength < 128 ? 1 : 2); + } + private: + PointerVector m_SubRecords; + }; + + /** + * @class Asn1SequenceRecord + * Represents an ASN.1 record with a value of type Sequence + */ + class Asn1SequenceRecord : public Asn1ConstructedRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Sequence + * @param subRecords A list of sub-records to assign as the record value + */ + explicit Asn1SequenceRecord(const std::vector& subRecords); + + /** + * A constructor to create a record of type Sequence + * @param subRecords A PointerVector of sub-records to assign as the record value + */ + explicit Asn1SequenceRecord(const PointerVector& subRecords); + + private: + Asn1SequenceRecord() = default; + }; + + /** + * @class Asn1SetRecord + * Represents an ASN.1 record with a value of type Set + */ + class Asn1SetRecord : public Asn1ConstructedRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Set + * @param subRecords A list of sub-records to assign as the record value + */ + explicit Asn1SetRecord(const std::vector& subRecords); + + /** + * A constructor to create a record of type Set + * @param subRecords A PointerVector of sub-records to assign as the record value + */ + explicit Asn1SetRecord(const PointerVector& subRecords); + + private: + Asn1SetRecord() = default; + }; + + /** + * @class Asn1PrimitiveRecord + * Represents a primitive ASN.1 record, meaning a record that doesn't have sub-records. + * This is an abstract class that cannot be instantiated + */ + class Asn1PrimitiveRecord : public Asn1Record + { + friend class Asn1Record; + + protected: + Asn1PrimitiveRecord() = default; + explicit Asn1PrimitiveRecord(Asn1UniversalTagType tagType); + }; + + /** + * @class Asn1IntegerRecord + * Represents an ASN.1 record with a value of type Integer + */ + class Asn1IntegerRecord : public Asn1PrimitiveRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Integer + * @param value An integer to set as the record value + */ + explicit Asn1IntegerRecord(uint32_t value); + + /** + * @return The integer value of this record + */ + uint32_t getValue() { decodeValueIfNeeded(); return m_Value; } + + protected: + Asn1IntegerRecord() = default; + + void decodeValue(uint8_t* data, bool lazy) override; + std::vector encodeValue() const override; + + std::vector toStringList() override; + + private: + uint32_t m_Value = 0; + }; + + /** + * @class Asn1EnumeratedRecord + * Represents an ASN.1 record with a value of type Enumerated + */ + class Asn1EnumeratedRecord : public Asn1IntegerRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Enumerated + * @param value An integer to set as the record value + */ + explicit Asn1EnumeratedRecord(uint32_t value); + + private: + Asn1EnumeratedRecord() = default; + }; + + /** + * @class Asn1OctetStringRecord + * Represents an ASN.1 record with a value of type Octet String + */ + class Asn1OctetStringRecord : public Asn1PrimitiveRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Octet String from a printable value + * @param value A string to set as the record value + */ + explicit Asn1OctetStringRecord(const std::string& value); + + /** + * A constructor to create a record of type Octet String from a non-printable value + * @param value A byte array to set as the record value + * @param valueLength The length of the byte array + */ + explicit Asn1OctetStringRecord(const uint8_t* value, size_t valueLength); + + /** + * @return The string value of this record + */ + std::string getValue() { decodeValueIfNeeded(); return m_Value; }; + + protected: + void decodeValue(uint8_t* data, bool lazy) override; + std::vector encodeValue() const override; + + std::vector toStringList() override; + + private: + std::string m_Value; + bool m_IsPrintable = true; + + Asn1OctetStringRecord() = default; + }; + + /** + * @class Asn1BooleanRecord + * Represents an ASN.1 record with a value of type Boolean + */ + class Asn1BooleanRecord : public Asn1PrimitiveRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Boolean + * @param value A boolean to set as the record value + */ + explicit Asn1BooleanRecord(bool value); + + /** + * @return The boolean value of this record + */ + bool getValue() { decodeValueIfNeeded(); return m_Value; }; + + protected: + void decodeValue(uint8_t* data, bool lazy) override; + std::vector encodeValue() const override; + + std::vector toStringList() override; + + private: + Asn1BooleanRecord() = default; + + bool m_Value = false; + }; + + /** + * @class Asn1NullRecord + * Represents an ASN.1 record with a value of type Null + */ + class Asn1NullRecord : public Asn1PrimitiveRecord + { + friend class Asn1Record; + + public: + /** + * A constructor to create a record of type Null + */ + Asn1NullRecord(); + + protected: + void decodeValue(uint8_t* data, bool lazy) override {} + std::vector encodeValue() const override { return {}; } + }; +} diff --git a/Packet++/header/HttpLayer.h b/Packet++/header/HttpLayer.h index ca43fa3dd1..41784e0549 100644 --- a/Packet++/header/HttpLayer.h +++ b/Packet++/header/HttpLayer.h @@ -1,20 +1,10 @@ #pragma once +#include "DeprecationUtils.h" #include "TextBasedProtocol.h" #include #include -#ifndef PCPP_DEPRECATED -#if defined(__GNUC__) || defined(__clang__) -#define PCPP_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define PCPP_DEPRECATED __declspec(deprecated) -#else -#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") -#define PCPP_DEPRECATED -#endif -#endif - /// @file /** @@ -524,7 +514,8 @@ namespace pcpp * default status code string * @deprecated Use other constructors instead. */ - PCPP_DEPRECATED explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, const std::string& statusCodeString); + PCPP_DEPRECATED("Use other constructors instead") + explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, const std::string& statusCodeString); /** * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. @@ -742,7 +733,8 @@ namespace pcpp * @return True if setting the status code was completed successfully, false otherwise * @deprecated Use the other overload instead. */ - PCPP_DEPRECATED bool setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString); + PCPP_DEPRECATED("Use the other overload instead") + bool setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString); /** * Set the status code diff --git a/Packet++/header/IPv4Layer.h b/Packet++/header/IPv4Layer.h index b987090224..7e1e826333 100644 --- a/Packet++/header/IPv4Layer.h +++ b/Packet++/header/IPv4Layer.h @@ -242,9 +242,9 @@ namespace pcpp if (dataSize < 2) return res; - uint8_t valueOffset = (uint8_t)(1); + uint8_t valueOffset = static_cast(1); - while ((size_t)valueOffset < dataSize) + while (static_cast(valueOffset) < dataSize) { uint32_t curValue; memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); @@ -253,7 +253,7 @@ namespace pcpp res.push_back(IPv4Address(curValue)); - valueOffset += (uint8_t)(4); + valueOffset += static_cast(4); } return res; @@ -281,12 +281,12 @@ namespace pcpp if (dataSize < 2) return res; - res.type = (IPv4TimestampOptionValue::TimestampType)m_Data->recordValue[1]; + res.type = static_cast(m_Data->recordValue[1]); - uint8_t valueOffset = (uint8_t)(2); + uint8_t valueOffset = static_cast(2); bool readIPAddr = (res.type == IPv4TimestampOptionValue::TimestampAndIP); - while ((size_t)valueOffset < dataSize) + while (static_cast(valueOffset) < dataSize) { uint32_t curValue; memcpy(&curValue, m_Data->recordValue + valueOffset, sizeof(uint32_t)); @@ -301,7 +301,7 @@ namespace pcpp if (res.type == IPv4TimestampOptionValue::TimestampAndIP) readIPAddr = !readIPAddr; - valueOffset += (uint8_t)(4); + valueOffset += static_cast(4); } return res; @@ -323,14 +323,14 @@ namespace pcpp */ static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen) { - auto data = (TLVRawData*)recordRawData; + auto data = reinterpret_cast(recordRawData); if (data == nullptr) return false; if (tlvDataLen < sizeof(TLVRawData::recordType)) return false; - if (getIPv4OptionType(data) == (uint8_t)IPV4OPT_EndOfOptionsList || data->recordType == (uint8_t)IPV4OPT_NOP) + if (getIPv4OptionType(data) == static_cast(IPV4OPT_EndOfOptionsList) || data->recordType == static_cast(IPV4OPT_NOP)) return true; return TLVRecord::canAssign(recordRawData, tlvDataLen); @@ -343,10 +343,10 @@ namespace pcpp if (m_Data == nullptr) return 0; - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) + if (getIPv4OptionType() == static_cast(IPV4OPT_EndOfOptionsList) || m_Data->recordType == static_cast(IPV4OPT_NOP)) return sizeof(uint8_t); - return (size_t)m_Data->recordLen; + return static_cast(m_Data->recordLen); } size_t getDataSize() const @@ -354,10 +354,10 @@ namespace pcpp if (m_Data == nullptr) return 0; - if (getIPv4OptionType() == (uint8_t)IPV4OPT_EndOfOptionsList || m_Data->recordType == (uint8_t)IPV4OPT_NOP) - return (size_t)0; + if (getIPv4OptionType() == static_cast(IPV4OPT_EndOfOptionsList) || m_Data->recordType == static_cast(IPV4OPT_NOP)) + return 0; - return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); + return static_cast(m_Data->recordLen) - (2*sizeof(uint8_t)); } private: @@ -369,7 +369,7 @@ namespace pcpp if (data == nullptr) return IPV4OPT_Unknown; - return (IPv4OptionTypes)data->recordType; + return static_cast(data->recordType); } }; @@ -637,7 +637,7 @@ namespace pcpp /** * @return Size of IPv4 header (including IPv4 options if exist) */ - size_t getHeaderLen() const { return (size_t)((uint16_t)(getIPv4Header()->internetHeaderLength) * 4) + m_TempHeaderExtension; } + size_t getHeaderLen() const { return static_cast(static_cast(getIPv4Header()->internetHeaderLength) * 4) + m_TempHeaderExtension; } /** * Calculate the following fields: diff --git a/Packet++/header/IPv6Layer.h b/Packet++/header/IPv6Layer.h index 584dbcae76..f603083d59 100644 --- a/Packet++/header/IPv6Layer.h +++ b/Packet++/header/IPv6Layer.h @@ -231,19 +231,19 @@ namespace pcpp while (curExt != NULL && dynamic_cast(curExt) == NULL) curExt = curExt->getNextHeader(); - return (TIPv6Extension*)curExt; + return static_cast(curExt); } template TIPv6Extension* IPv6Layer::addExtension(const TIPv6Extension& extensionHeader) { - int offsetToAddHeader = (int)getHeaderLen(); + int offsetToAddHeader = static_cast(getHeaderLen()); if (!extendLayer(offsetToAddHeader, extensionHeader.getExtensionLen())) { return NULL; } - TIPv6Extension* newHeader = new TIPv6Extension(this, (size_t)offsetToAddHeader); + TIPv6Extension* newHeader = new TIPv6Extension(this, static_cast(offsetToAddHeader)); (*newHeader) = extensionHeader; if (m_FirstExtension != NULL) diff --git a/Packet++/header/LdapLayer.h b/Packet++/header/LdapLayer.h new file mode 100644 index 0000000000..92751cecd5 --- /dev/null +++ b/Packet++/header/LdapLayer.h @@ -0,0 +1,1278 @@ +#pragma once + +#include "Layer.h" +#include "Asn1Codec.h" +#include +#include +#include + +/// @file + +/** + * @namespace pcpp + * @brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + /** + * @class LdapOperationType + * @brief An enum wrapper class for LDAP operation types + */ + class LdapOperationType + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /// Bind Request + BindRequest = 0, + /// Bind Response + BindResponse = 1, + /// Unbind Request + UnbindRequest = 2, + /// Search Request + SearchRequest = 3, + /// Search Result Entry + SearchResultEntry = 4, + /// Search Result Done + SearchResultDone = 5, + /// Modify Request + ModifyRequest = 6, + /// Modify Response + ModifyResponse = 7, + /// Add Request + AddRequest = 8, + /// Add Response + AddResponse = 9, + /// Delete Request + DeleteRequest = 10, + /// Delete Response + DeleteResponse = 11, + /// Modify DN (Distinguished Name) Request + ModifyDNRequest = 12, + /// Modify DN (Distinguished Name) Response + ModifyDNResponse = 13, + /// Compare Request + CompareRequest = 14, + /// Compare Response + CompareResponse = 15, + /// Abandon Request + AbandonRequest = 16, + /// Search Result Reference + SearchResultReference = 19, + /// Extended Request + ExtendedRequest = 23, + /// Extended Response + ExtendedResponse = 24, + /// Intermediate Response + IntermediateResponse = 25, + /// Unknown operation type + Unknown = 255 + }; + + LdapOperationType() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct LdapOperationType from Value enum + * @param[in] value the opetation type enum value + */ + constexpr LdapOperationType(Value value) : m_Value(value) {} + + /** + * @return A string representation of the operation type + */ + std::string toString() const; + + /** + * A static method that creates LdapOperationType from an integer value + * @param[in] value The operation type integer value + * @return The operation type that corresponds to the integer value. If the integer value + * doesn't corresponds to any operation type, LdapOperationType::Unknown is returned + */ + static LdapOperationType fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapOperationType) + explicit operator bool() const = delete; + + private: + Value m_Value = LdapOperationType::Unknown; + }; + + /** + * @class LdapResultCode + * @brief An enum wrapper class for LDAP result codes + */ + class LdapResultCode + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /** + * Indicates that the associated operation completed successfully + */ + Success = 0, + /** + * Indicates that there was a problem with the client’s use of the LDAP protocol + */ + OperationsError= 1, + /** + * Indicates that there was a problem with the client’s use of the LDAP protocol + */ + ProtocolError = 2, + /** + * Indicates that the associated operation failed because it hadn’t completed by the time + * a maximum processing time limit had been reached + */ + TimeLimitExceeded = 3, + /** + * Indicates that the associated search operation failed because the server has determined + * that the number of entries that would be returned in response to the search would exceed + * the upper bound for that operation + */ + SizeLimitExceeded = 4, + /** + * Indicates that the associated compare request targeted an entry that exists and that contains + * the targeted attribute, but does not have any value that matches the provided assertion value + */ + CompareFalse = 5, + /** + * Indicates that the associated compare request targeted an entry that exists and that contains + * the targeted attribute with a value that matches the provided assertion value + */ + CompareTrue = 6, + /** + * Indicates that the associated bind operation failed because the client attempted to authenticate + * with a mechanism that the server does not support or that it does not allow the client to use + */ + AuthMethodNotSupported = 7, + /** + * Indicates that the server requires the client to authenticate with a stronger form of authentication + */ + StrongerAuthRequired = 8, + /** + * Indicates that the request cannot be processed exactly as issued, but that it might succeed + * if re-issued to a different server, or is updated to target a different location in the DIT + */ + Referral = 10, + /** + * Indicates that some administrative limit within the server was exceeded while processing the request + */ + AdminLimitExceeded = 11, + /** + * Indicates that the request includes a control with a criticality of true, + * but that control could not be honored for some reason + */ + UnavailableCriticalExtension = 12, + /** + * Indicates that the server is only willing to process the requested operation if it is received + * over a secure connection that does not allow an eavesdropper to decipher or alter the contents + * of the request or response + */ + ConfidentialityRequired = 13, + /** + * Indicates that the server has completed a portion of the processing for the provided SASL + * bind request, but that it needs additional information from the client to complete the authentication + */ + SaslBindInProgress = 14, + /** + * Indicates that the request targeted an attribute that does not exist in the specified entry + */ + NoSuchAttribute = 16, + /** + * Indicates that the request attempted to provide one or more values for an attribute type + * that is not defined in the server schema + */ + UndefinedAttributeType = 17, + /** + * Indicates that the search request tried to perform some type of matching that is not + * supported for the target attribute type + */ + InappropriateMatching = 18, + /** + * Indicates that the requested operation would have resulted in an entry that violates + * some constraint defined within the server + */ + ConstraintViolation = 19, + /** + * Indicates that the requested operation would have resulted in an attribute in which + * the same value appeared more than once + */ + AttributeOrValueExists = 20, + /** + * Indicates that the requested add or modify operation would have resulted in an entry + * that had at least one attribute value that does not conform to the constraints of the + * associated attribute syntax + */ + InvalidAttributeSyntax = 21, + /** + * Indicates that the requested operation targeted an entry that does not exist within the DIT + */ + NoSuchObject = 32, + /** + * Indicates that a problem occurred while attempting to dereference an alias during search processing + */ + AliasProblem = 33, + /** + * Indicates that the request included a malformed entry DN + */ + InvalidDNSyntax = 34, + /** + * Indicates that the server encountered an alias while processing the request and that there + * was some problem related to that alias + */ + AliasDereferencingProblem = 36, + /** + * Indicates that the client attempted to bind in an inappropriate manner that is inappropriate + * for the target account + */ + InappropriateAuthentication = 48, + /** + * Indicates that the client attempted to bind with a set of credentials that cannot + * be used to authenticate + */ + InvalidCredentials = 49, + /** + * Indicates that the client requested an operation for which it does not have the necessary + * access control permissions + */ + InsufficientAccessRights = 50, + /** + * Indicates that the requested operation cannot be processed because the server is currently too busy + */ + Busy = 51, + /** + * Indicates that the server is currently not available to process the requested operation + */ + Unavailable = 52, + /** + * Indicates that the server is not willing to process the requested operation for some reason + */ + UnwillingToPerform = 53, + /** + * Indicates that the server detected some kind of circular reference in the course + * of processing an operation + */ + LoopDetect = 54, + /** + * Indicates that the requested add or modify DN operation would have resulted in an entry + * that violates some naming constraint within the server + */ + NamingViolation = 64, + /** + * Indicates that the requested operation would have resulted in an entry that has + * an inappropriate set of object classes, or whose attributes violate the constraints + * associated with its set of object classes + */ + ObjectClassViolation = 65, + /** + * Indicates that the requested operation is only supported for leaf entries, + * but the targeted entry has one or more subordinates + */ + NotAllowedOnNonLeaf = 66, + /** + * Indicates that the requested modify operation would have resulted in an entry that + * does not include all of the attributes used in its RDN + */ + NotAllowedOnRDN = 67, + /** + * Indicates that the requested operation would have resulted in an entry with the same + * DN as an entry that already exists in the server + */ + EntryAlreadyExists = 68, + /** + * Indicates that the requested modify operation would have altered the target entry’s + * set of object classes in a way that is not supported + */ + ObjectClassModsProhibited = 69, + /** + * Indicates that the requested operation would have required manipulating information + * in multiple servers in a way that is not supported + */ + AffectsMultipleDSAs = 71, + /** + * Used when a problem occurs for which none of the other result codes is more appropriate + */ + Other = 80, + /** + * Unknown result code + */ + Unknown = 255 + }; + + LdapResultCode() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct LdapResultCode from Value enum + * @param[in] value the result code enum value + */ + constexpr LdapResultCode(Value value) : m_Value(value) { } + + /** + * @return A string representation of the result code + */ + std::string toString() const; + + /** + * A static method that creates LdapResultCode from an integer value + * @param[in] value The result code integer value + * @return The result code that corresponds to the integer value. If the integer value + * doesn't corresponds to any operation type, LdapResultCode::Unknown is returned + */ + static LdapResultCode fromUintValue(uint8_t value); + + // Allow switch and comparisons + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapResultCode) + explicit operator bool() const = delete; + private: + Value m_Value = LdapResultCode::Unknown; + }; + + /** + * @struct LdapControl + * A struct that represents an LDAP Control + */ + struct LdapControl + { + /// LDAP control type + std::string controlType; + /// LDAP control value + std::string controlValue; + + /** + * Equality operator overload for this struct + * @param[in] other The value to compare with + * @return True if both values are equal, false otherwise + */ + bool operator==(const LdapControl& other) const + { + return controlType == other.controlType && controlValue == other.controlValue; + } + }; + + /** + * @struct LdapAttribute + * A struct that represents an LDAP attribute + */ + struct LdapAttribute + { + /// Attribute description + std::string type; + /// A list of attribute values (zero or more) + std::vector values; + + /** + * Equality operator overload for this struct + * @param[in] other The value to compare with + * @return True if both values are equal, false otherwise + */ + bool operator==(const LdapAttribute& other) const + { + return type == other.type && values == other.values; + } + }; + + /** + * @class LdapLayer + * Represents an LDAP message + */ + class LdapLayer : public Layer + { + public: + /** + * A constructor to create a new LDAP message + * @param[in] messageId The LDAP message ID + * @param[in] operationType The LDAP operation type + * @param[in] messageRecords A vector of ASN.1 records that comprise the LDAP message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapLayer(uint16_t messageId, LdapOperationType operationType, + const std::vector& messageRecords, + const std::vector& controls = std::vector()); + + ~LdapLayer() {} + + /** + * @return The root ASN.1 record of the LDAP message. All of the message data will be under this record. + * If the Root ASN.1 record is malformed, an exception is thrown + */ + Asn1SequenceRecord* getRootAsn1Record() const; + + /** + * @return The ASN.1 record of the specific LDAP operation in this LDAP message. Each operation has a specific + * structure. If the Operation ASN.1 record is malformed, an exception is thrown + */ + Asn1ConstructedRecord* getLdapOperationAsn1Record() const; + + /** + * @return The LDAP message ID. If the ASN.1 record is malformed, an exception is thrown + */ + uint16_t getMessageID() const; + + /** + * @return A vector of LDAP controls in this message. If the message contains no controls then an empty + * vector is returned. If the Controls ASN.1 record is malformed, an exception is thrown + */ + std::vector getControls() const; + + /** + * @return The LDAP operation of this message. If the Operation ASN.1 record is malformed, an exception is thrown + */ + virtual LdapOperationType getLdapOperationType() const; + + /** + * Most getter methods in this class throw an exception if the corresponding ASN.1 record is invalid. + * This is a wrapper method that allows calling these getters without adding a `try...catch` clause. + * It accepts the getter method and an out variable. It tries to call the getter and if no exception + * is thrown, the out variable will contain the result. + * + * Here is an example: + * @code + * uint16_t messageId; + * ldapLayer->tryGet(&pcpp::LdapLayer::getMessageID, messageId)); + * @endcode + * + * We call getMessageID(), if no exception is thrown the variable messageId will hold the result + * + * @tparam Method The class method type + * @tparam ResultType The expected result type (for example: uint8_t, std::string, etc.) + * @param[in] method The class method to call + * @param[out] result An outvariable to contain the result if no exception is thrown + * @return True if no exception was thrown or false otherwise + */ + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + + /** + * A static method that checks whether a source or dest port match those associated with the LDAP protocol + * @param[in] port The port number to check + * @return True if this is an LDAP port, false otherwise + */ + static bool isLdapPort(uint16_t port) { return port == 389; } + + /** + * A static message to parse an LDAP message from raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + * @return An instance of LdapLayer if this is indeed an LDAP message, nullptr otherwise + */ + static LdapLayer* parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + // implement abstract methods + + /** + * Tries to identify more LDAP messages in this packet if exist + */ + void parseNextLayer() override; + + /** + * @return The size of the LDAP message + */ + size_t getHeaderLen() const override { return m_Asn1Record->getTotalLength(); } + + void computeCalculateFields() override {} + + OsiModelLayer getOsiModelLayer() const override { return OsiModelApplicationLayer; } + + std::string toString() const override; + + protected: + std::unique_ptr m_Asn1Record; + + LdapLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + LdapLayer() = default; + void init(uint16_t messageId, LdapOperationType operationType, const std::vector& messageRecords, const std::vector& controls); + virtual std::string getExtendedInfoString() const { return ""; } + + static constexpr int messageIdIndex = 0; + static constexpr int operationTypeIndex = 1; + static constexpr int controlsIndex = 2; + + static constexpr int controlTypeIndex = 0; + static constexpr int controlValueIndex = 1; + + template + bool internalTryGet(LdapClass* thisPtr, Method method, ResultType& result) + { + try + { + result = std::mem_fn(method)(thisPtr); + return true; + } + catch (...) + { + return false; + } + } + }; + + /** + * @class LdapResponseLayer + * An abstract class for representing an LDAP response message. It's the parent class + * for all response message layers + */ + class LdapResponseLayer : public LdapLayer + { + public: + /** + * @return LDAP result code + */ + LdapResultCode getResultCode() const; + + /** + * @return An optional distinguished name (DN) that may be included in the response to a request + * targeting an entry that does not exist + */ + std::string getMatchedDN() const; + + /** + * @return An optional string that can provide additional information about the processing that + * was performed + */ + std::string getDiagnosticMessage() const; + + /** + * @return An optional list of one or more URIs that the client may use to re-try the operation + * somewhere else. If referral doesn't exist on the message, and empty vector is returned + */ + std::vector getReferral() const; + protected: + static constexpr int resultCodeIndex = 0; + static constexpr int matchedDNIndex = 1; + static constexpr int diagnotsticsMessageIndex = 2; + static constexpr int referralIndex = 3; + + static constexpr uint8_t referralTagType = 3; + + LdapResponseLayer() = default; + LdapResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + + LdapResponseLayer(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode, + const std::string& matchedDN, const std::string& diagnosticMessage, + const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()); + + void init(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode, + const std::string& matchedDN, const std::string& diagnosticMessage, + const std::vector& referral = std::vector(), + const std::vector& additionalRecords = std::vector(), + const std::vector& controls = std::vector()); + + std::string getExtendedInfoString() const override; + }; + + /** + * @class LdapBindRequestLayer + * Represents LDAP bind request operation + */ + class LdapBindRequestLayer : public LdapLayer + { + public: + /** + * An enum to represent the bind request authentication type + */ + enum class AuthenticationType : uint8_t + { + /// Simple authentication + Simple = 0, + /// SASL authentication + Sasl = 3, + /// Unknown / not application authentication type + NotApplicable = 255 + }; + + /** + * @struct SaslAuthentication + * A struct to represent SASL authentication + */ + struct SaslAuthentication + { + /// The SASL mechanism + std::string mechanism; + /// Encoded SASL credentials + std::vector credentials; + + /** + * Equality operator overload for this struct + * @param[in] other The value to compare with + * @return True if both values are equal, false otherwise + */ + bool operator==(const SaslAuthentication& other) const + { + return mechanism == other.mechanism && credentials == other.credentials; + } + + /** + * Inequality operator overload for this struct + * @param[in] other The value to compare with + * @return False if both values are equal, true otherwise + */ + bool operator!=(const SaslAuthentication& other) const + { + return !operator==(other); + } + }; + + /** + * A constructor to create a new LDAP bind request message with simple authentication + * @param[in] messageId The LDAP message ID + * @param[in] version The LDAP protocol version that the client wants to use + * @param[in] name The DN of the user to authenticate + * @param[in] simpleAuthentication Simple authentication to use in this message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapBindRequestLayer( + uint16_t messageId, uint8_t version, const std::string& name, const std::string& simpleAuthentication, + const std::vector& controls = std::vector()); + + /** + * A constructor to create a new LDAP bind request message with SASL authentication + * @param[in] messageId The LDAP message ID + * @param[in] version The LDAP protocol version that the client wants to use + * @param[in] name The DN of the user to authenticate + * @param[in] saslAuthentication SASL authentication to use in this message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapBindRequestLayer( + uint16_t messageId, uint8_t version, const std::string& name, const SaslAuthentication& saslAuthentication, + const std::vector& controls = std::vector()); + + /** + * @return The LDAP protocol version that the client wants to use + */ + uint32_t getVersion() const; + + /** + * @return The DN of the user to authenticate + */ + std::string getName() const; + + /** + * @return The authentication type included in this message + */ + AuthenticationType getAuthenticationType() const; + + /** + * @return The simple authentication included in this message + * @throws std::invalid_argument if the message doesn't include simple authentication + */ + std::string getSimpleAuthentication() const; + + /** + * @return The SASL authentication included in this message + * @throws std::invalid_argument if the message doesn't include SASL authentication + */ + SaslAuthentication getSaslAuthentication() const; + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapBindRequestLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + + std::string getExtendedInfoString() const override; + private: + static constexpr int versionIndex = 0; + static constexpr int nameIndex = 1; + static constexpr int credentialIndex = 2; + + static constexpr int saslMechanismIndex = 0; + static constexpr int saslCredentialsIndex = 1; + }; + + /** + * @class LdapBindResponseLayer + * Represents LDAP bind response operation + */ + class LdapBindResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP bind response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] serverSaslCredentials Encoded server SASL credentials for use in subsequent processing + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapBindResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& serverSaslCredentials = std::vector(), + const std::vector& controls = std::vector()); + + /** + * @return Encoded server SASL credentials for use in subsequent processing + */ + std::vector getServerSaslCredentials() const; + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + static constexpr int serverSaslCredentialsTagType = 7; + + LdapBindResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapUnbindRequestLayer + * Represents LDAP unbind operation + */ + class LdapUnbindRequestLayer : public LdapLayer + { + public: + /** + * A constructor to create a new LDAP unbind message + * @param[in] messageId The LDAP message ID + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + explicit LdapUnbindRequestLayer(uint16_t messageId, const std::vector& controls = std::vector()); + + // Unbind request has no operation record + Asn1ConstructedRecord* getLdapOperationAsn1Record() const = delete; + + LdapOperationType getLdapOperationType() const override { return LdapOperationType::UnbindRequest; } + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapUnbindRequestLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapSearchRequestLayer + * Represents LDAP search request operation + */ + class LdapSearchRequestLayer : public LdapLayer + { + public: + /** + * @class SearchRequestScope + * An enum wrapper class for LDAP search request scope + */ + class SearchRequestScope + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /** + * The search operation should only be performed against the entry specified as the search base DN + */ + BaseObject = 0, + /** + * The search operation should only be performed against entries that are immediate subordinates + * of the entry specified as the search base DN + */ + SingleLevel = 1, + /** + * The search operation should be performed against the entry specified as the search base + * and all of its subordinates to any depth + */ + WholeSubtree = 2, + /** + * The search operation should be performed against any subordinate entries (to any depth) below the + * entry specified by the base DN should be considered, but the base entry itself + * should not be considered + */ + subordinateSubtree = 3, + /** + * Unknown or unsupported scope + */ + Unknown = 255 + }; + + SearchRequestScope() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct SearchRequestScope from Value enum + * @param[in] value the scope enum value + */ + constexpr SearchRequestScope(Value value) : m_Value(value) {} + + /** + * @return A string representation of the scope value + */ + std::string toString() const; + + /** + * A static method that creates SearchRequestScope from an integer value + * @param[in] value The scope integer value + * @return The scope that corresponds to the integer value. If the integer value + * doesn't corresponds to any enum value, SearchRequestScope::Unknown is returned + */ + static SearchRequestScope fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapOperationType) + explicit operator bool() const = delete; + private: + Value m_Value = SearchRequestScope::Unknown; + }; + + /** + * @class DerefAliases + * An enum wrapper class for LDAP search request dereferencing aliases + */ + class DerefAliases + { + public: + /** + * Define enum types and the corresponding int values + */ + enum Value : uint8_t + { + /// Never dereferences aliases + NeverDerefAliases = 0, + /// Dereferences aliases only after name resolution + DerefInSearching = 1, + /// Dereferences aliases only during name resolution + DerefFindingBaseObj = 2, + /// Always dereference aliases + DerefAlways = 3, + /// Unknown value + Unknown = 255 + }; + + DerefAliases() = default; + + // cppcheck-suppress noExplicitConstructor + /** + * Construct DerefAliases from Value enum + * @param[in] value the dereference alias enum value + */ + constexpr DerefAliases(Value value) : m_Value(value) {} + + /** + * @return A string representation of the dereference alias value + */ + std::string toString() const; + + /** + * A static method that creates DerefAliases from an integer value + * @param[in] value The dereference alias integer value + * @return The dereference alias that corresponds to the integer value. If the integer value + * doesn't corresponds to any enum value, DerefAliases::Unknown is returned + */ + static DerefAliases fromUintValue(uint8_t value); + + // Allow switch and comparisons. + constexpr operator Value() const { return m_Value; } + + // Prevent usage: if(LdapOperationType) + explicit operator bool() const = delete; + private: + Value m_Value = DerefAliases::Unknown; + }; + + /** + * A constructor to create a new LDAP search request message + * @param[in] messageId The LDAP message ID + * @param[in] baseObject The base object for the LDAP search request entry + * @param[in] scope The portion of the target subtree that should be considered + * @param[in] derefAliases The alias dereferencing behavior, which indicates how the server should treat + * any aliases encountered while processing the search + * @param[in] sizeLimit The maximum number of entries that should be returned from the search + * @param[in] timeLimit The time limit for the search in seconds + * @param[in] typesOnly If this is given a value of true, then it indicates that entries that match the + * search criteria should be returned containing only the attribute descriptions for the attributes + * contained in that entry but should not include the values for those attributes. + * If this is given a value of false, then it indicates that the attribute values should be included + * in the entries that are returned + * @param[in] filterRecord The filter for the search. Please note that parsing for the search filter + * doesn't exist yet. Therefore, the expected input value should be a plain ASN.1 record + * @param[in] attributes A set of attributes to request for inclusion in entries that match the search + * criteria and are returned to the client + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapSearchRequestLayer( + uint16_t messageId, const std::string& baseObject, SearchRequestScope scope, DerefAliases derefAliases, + uint8_t sizeLimit, uint8_t timeLimit, bool typesOnly, Asn1Record* filterRecord, + const std::vector& attributes, const std::vector& controls = std::vector()); + + /** + * @return The base object for the LDAP search request entry + */ + std::string getBaseObject() const; + + /** + * @return The portion of the target subtree that should be considered + */ + SearchRequestScope getScope() const; + + /** + * @return The alias dereferencing behavior + */ + DerefAliases getDerefAlias() const; + + /** + * @return The maximum number of entries that should be returned from the search + */ + uint8_t getSizeLimit() const; + + /** + * @return The time limit for the search in seconds + */ + uint8_t getTimeLimit() const; + + /** + * @return If this flag is true, then it indicates that entries that match the search criteria should be + * returned containing only the attribute descriptions for the attributes contained in that entry but + * should not include the values for those attributes. If this flag is false, then it indicates that the + * attribute values should be included in the entries that are returned + */ + bool getTypesOnly() const; + + /** + * @return The filter for the search. Please note that parsing for the search filter doesn't exist yet. + * Therefore, the return value is a plain ASN.1 record + */ + Asn1Record* getFilter() const; + + /** + * @return A list of search request attributes + */ + std::vector getAttributes() const; + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + static constexpr int baseObjectIndex = 0; + static constexpr int scopeIndex = 1; + static constexpr int derefAliasIndex = 2; + static constexpr int sizeLimitIndex = 3; + static constexpr int timeLimitIndex = 4; + static constexpr int typesOnlyIndex = 5; + static constexpr int filterIndex = 6; + static constexpr int attributesIndex = 7; + + LdapSearchRequestLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + + + std::string getExtendedInfoString() const override; + }; + + /** + * @class LdapSearchResultEntryLayer + * Represents LDAP search result entry message + */ + class LdapSearchResultEntryLayer : public LdapLayer + { + public: + /** + * A constructor to create a new LDAP search result entry message + * @param[in] messageId The LDAP message ID + * @param[in] objectName The entry's DN + * @param[in] attributes The entry's attributes + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapSearchResultEntryLayer(uint16_t messageId, const std::string& objectName, + const std::vector& attributes, + const std::vector& controls = std::vector()); + + /** + * @return The entry's DN + */ + std::string getObjectName() const; + + /** + * @return The entry's attributes + */ + std::vector getAttributes() const; + + template + bool tryGet(Method method, ResultType& result) + { + return internalTryGet(this, method, result); + } + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + static constexpr int objectNameIndex = 0; + static constexpr int attributesIndex = 1; + static constexpr int attributeTypeIndex = 0; + static constexpr int attributeValueIndex = 1; + + LdapSearchResultEntryLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapSearchResultDoneLayer + * Represents LDAP search result done message + */ + class LdapSearchResultDoneLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP search result done message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapSearchResultDoneLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::SearchResultDone, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapSearchResultDoneLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapModifyResponseLayer + * Represents LDAP modify response message + */ + class LdapModifyResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP modify response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapModifyResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::ModifyResponse, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapModifyResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapAddResponseLayer + * Represents LDAP add response message + */ + class LdapAddResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP add response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapAddResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::AddResponse, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapAddResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapDeleteResponseLayer + * Represents LDAP delete response message + */ + class LdapDeleteResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP delete response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapDeleteResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::DeleteResponse, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapDeleteResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapModifyDNResponseLayer + * Represents LDAP modify DN response message + */ + class LdapModifyDNResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP modify DN response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapModifyDNResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::ModifyDNResponse, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapModifyDNResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; + + /** + * @class LdapCompareResponseLayer + * Represents LDAP compare response message + */ + class LdapCompareResponseLayer : public LdapResponseLayer + { + public: + /** + * A constructor to create a new LDAP compare response message + * @param[in] messageId The LDAP message ID + * @param[in] resultCode The LDAP result code + * @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable + * pass an empty string + * @param[in] diagnosticMessage The additional information to set on the message. If not applicable + * pass an empty string + * @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional + * parameter. If not provided then referral won't be added to the message + * @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message + * will be created without LDAP controls + */ + LdapCompareResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN, + const std::string& diagnosticMessage, const std::vector& referral = std::vector(), + const std::vector& controls = std::vector()) + : LdapResponseLayer(messageId, LdapOperationType::CompareResponse, resultCode, matchedDN, diagnosticMessage, referral, controls) {} + protected: + friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + LdapCompareResponseLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet) {} + }; +} // namespace pcpp + +inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapControl& control) +{ + os << "{" << control.controlType << ", " << control.controlValue << "}"; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapAttribute& attr) +{ + os << "{" << attr.type << ", {"; + + std::string separator; + for (const auto& value : attr.values) + { + os << separator << value; + if (separator.empty()) + { + separator = ", "; + } + } + + os << "}}"; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapBindRequestLayer::SaslAuthentication& saslAuthentication) +{ + os << "{" << saslAuthentication.mechanism << ", {"; + + std::string separator; + for (const auto& value : saslAuthentication.credentials) + { + os << separator << "0x" << std::hex << static_cast(value) << std::dec; + if (separator.empty()) + { + separator = ", "; + } + } + + os << "}}"; + return os; +} diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index 85a85a7f4a..0f7cba2bbd 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -342,6 +342,11 @@ namespace pcpp */ const ProtocolType SMTP = 54; + /* + * LDAP protocol + */ + const ProtocolType LDAP = 55; + /** * An enum representing OSI model layers */ diff --git a/Packet++/header/SipLayer.h b/Packet++/header/SipLayer.h index 51ae99953f..740fcb5602 100644 --- a/Packet++/header/SipLayer.h +++ b/Packet++/header/SipLayer.h @@ -296,7 +296,7 @@ namespace pcpp Sip405MethodNotAllowed, /** The resource identified by the request is only capable of generating response entities that have content characteristics but not acceptable according to the Accept header field sent in the request */ Sip406NotAcceptable, - /** The request requires user authentication. This response is issued by proxys */ + /** The request requires user authentication. This response is issued by proxies */ Sip407ProxyAuthenticationRequired, /** Couldn't find the user in time. The server could not produce a response within a suitable amount of time, for example, if it could not determine the location of the user in time. The client MAY repeat the request without modifications at any later time */ Sip408RequestTimeout, diff --git a/Packet++/header/SomeIpSdLayer.h b/Packet++/header/SomeIpSdLayer.h index 0b8076e42f..2b0f49511f 100644 --- a/Packet++/header/SomeIpSdLayer.h +++ b/Packet++/header/SomeIpSdLayer.h @@ -1,6 +1,5 @@ #pragma once -#include "EndianPortable.h" #include "IpAddress.h" #include "Layer.h" #include "SomeIpLayer.h" diff --git a/Packet++/header/TLVData.h b/Packet++/header/TLVData.h index 7c56a58f1b..7ef0cfbbc3 100644 --- a/Packet++/header/TLVData.h +++ b/Packet++/header/TLVData.h @@ -69,7 +69,7 @@ namespace pcpp */ void assign(uint8_t* recordRawData) { - m_Data = (TLVRawData*)recordRawData; + m_Data = reinterpret_cast(recordRawData); } /** diff --git a/Packet++/header/TcpLayer.h b/Packet++/header/TcpLayer.h index f17832a90b..103c373c21 100644 --- a/Packet++/header/TcpLayer.h +++ b/Packet++/header/TcpLayer.h @@ -1,9 +1,13 @@ #pragma once +#include "DeprecationUtils.h" #include "Layer.h" #include "TLVData.h" #include +#define PCPP_DEPRECATED_TCP_OPTION_TYPE PCPP_DEPRECATED("enum TcpOptionType is deprecated; " \ + "Use enum class TcpOptionEnumType instead") + /// @file /** @@ -98,9 +102,9 @@ namespace pcpp TCPOPT_SACK_PERM = 4, /** SACK Block */ PCPP_TCPOPT_SACK = 5, - /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + /** Echo (obsoleted by option TcpOptionType::PCPP_TCPOPT_TIMESTAMP) */ TCPOPT_ECHO = 6, - /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + /** Echo Reply (obsoleted by option TcpOptionType::PCPP_TCPOPT_TIMESTAMP) */ TCPOPT_ECHOREPLY = 7, /** TCP Timestamps */ PCPP_TCPOPT_TIMESTAMP = 8, @@ -138,54 +142,111 @@ namespace pcpp TCPOPT_Unknown = 255 }; + /** + * TCP options types + */ + enum class TcpOptionEnumType : uint8_t + { + /** Padding */ + Nop = 1, + /** End of options */ + Eol = 0, + /** Segment size negotiating */ + Mss = 2, + /** Window scaling */ + Window = 3, + /** SACK Permitted */ + SackPerm = 4, + /** SACK Block */ + Sack = 5, + /** Echo (obsoleted by option TcpOptionEnumType::Timestamp) */ + Echo = 6, + /** Echo Reply (obsoleted by option TcpOptionEnumType::Timestamp) */ + EchoReply = 7, + /** TCP Timestamps */ + Timestamp = 8, + /** CC (obsolete) */ + Cc = 11, + /** CC.NEW (obsolete) */ + CcNew = 12, + /** CC.ECHO(obsolete) */ + CcEcho = 13, + /** MD5 Signature Option */ + Md5 = 19, + /** Multipath TCP */ + MpTcp = 0x1e, + /** SCPS Capabilities */ + Scps = 20, + /** SCPS SNACK */ + Snack = 21, + /** SCPS Record Boundary */ + RecBound = 22, + /** SCPS Corruption Experienced */ + CorrExp = 23, + /** Quick-Start Response */ + Qs = 27, + /** User Timeout Option (also, other known unauthorized use) */ + UserTo = 28, + /** RFC3692-style Experiment 1 (also improperly used for shipping products) */ + ExpFd = 0xfd, + /** RFC3692-style Experiment 2 (also improperly used for shipping products) */ + ExpFe = 0xfe, + /** Riverbed probe option, non IANA registered option number */ + RvbdProbe = 76, + /** Riverbed transparency option, non IANA registered option number */ + RvbdTrpy = 78, + /** Unknown option */ + Unknown = 255 + }; + // TCP option lengths - /** pcpp::PCPP_TCPOPT_NOP length */ + /** pcpp::TcpOptionEnumType::Nop length */ #define PCPP_TCPOLEN_NOP 1 - /** pcpp::PCPP_TCPOPT_EOL length */ + /** pcpp::TcpOptionEnumType::Eol length */ #define PCPP_TCPOLEN_EOL 1 - /** pcpp::TCPOPT_MSS length */ + /** pcpp::TcpOptionEnumType::Mss length */ #define PCPP_TCPOLEN_MSS 4 - /** pcpp::PCPP_TCPOPT_WINDOW length */ + /** pcpp::TcpOptionEnumType::Window length */ #define PCPP_TCPOLEN_WINDOW 3 - /** pcpp::TCPOPT_SACK_PERM length */ + /** pcpp::TcpOptionEnumType::SackPerm length */ #define PCPP_TCPOLEN_SACK_PERM 2 - /** pcpp::PCPP_TCPOPT_SACK length */ + /** pcpp::TcpOptionEnumType::Sack length */ #define PCPP_TCPOLEN_SACK_MIN 2 - /** pcpp::TCPOPT_ECHO length */ + /** pcpp::TcpOptionEnumType::Echo length */ #define PCPP_TCPOLEN_ECHO 6 - /** pcpp::TCPOPT_ECHOREPLY length */ + /** pcpp::TcpOptionEnumType::EchoReply length */ #define PCPP_TCPOLEN_ECHOREPLY 6 - /** pcpp::PCPP_TCPOPT_TIMESTAMP length */ + /** pcpp::TcpOptionEnumType::Timestamp length */ #define PCPP_TCPOLEN_TIMESTAMP 10 - /** pcpp::TCPOPT_CC length */ + /** pcpp::TcpOptionEnumType::Cc length */ #define PCPP_TCPOLEN_CC 6 - /** pcpp::TCPOPT_CCNEW length */ + /** pcpp::TcpOptionEnumType::CcNew length */ #define PCPP_TCPOLEN_CCNEW 6 - /** pcpp::TCPOPT_CCECHO length */ + /** pcpp::TcpOptionEnumType::CcEcho length */ #define PCPP_TCPOLEN_CCECHO 6 - /** pcpp::TCPOPT_MD5 length */ + /** pcpp::TcpOptionEnumType::Md5 length */ #define PCPP_TCPOLEN_MD5 18 - /** pcpp::TCPOPT_MPTCP length */ + /** pcpp::TcpOptionEnumType::MpTcp length */ #define PCPP_TCPOLEN_MPTCP_MIN 8 - /** pcpp::TCPOPT_SCPS length */ + /** pcpp::TcpOptionEnumType::Scps length */ #define PCPP_TCPOLEN_SCPS 4 - /** pcpp::TCPOPT_SNACK length */ + /** pcpp::TcpOptionEnumType::Snack length */ #define PCPP_TCPOLEN_SNACK 6 - /** pcpp::TCPOPT_RECBOUND length */ + /** pcpp::TcpOptionEnumType::RecBound length */ #define PCPP_TCPOLEN_RECBOUND 2 - /** pcpp::TCPOPT_CORREXP length */ + /** pcpp::TcpOptionEnumType::CorrExp length */ #define PCPP_TCPOLEN_CORREXP 2 - /** pcpp::TCPOPT_QS length */ + /** pcpp::TcpOptionEnumType::Qs length */ #define PCPP_TCPOLEN_QS 8 - /** pcpp::TCPOPT_USER_TO length */ + /** pcpp::TcpOptionEnumType::UserTo length */ #define PCPP_TCPOLEN_USER_TO 4 - /** pcpp::TCPOPT_RVBD_PROBE length */ + /** pcpp::TcpOptionEnumType::RvbdProbe length */ #define PCPP_TCPOLEN_RVBD_PROBE_MIN 3 - /** pcpp::TCPOPT_RVBD_TRPY length */ + /** pcpp::TcpOptionEnumType::RvbdTrpy length */ #define PCPP_TCPOLEN_RVBD_TRPY_MIN 16 - /** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */ + /** pcpp::TcpOptionEnumType::ExpFd and pcpp::TcpOptionEnumType::ExpFe length */ #define PCPP_TCPOLEN_EXP_MIN 2 @@ -208,17 +269,26 @@ namespace pcpp /** * A d'tor for this class, currently does nothing */ - ~TcpOption() { } + ~TcpOption() = default; /** - * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is null a value - * of ::TCPOPT_Unknown is returned + * @deprecated This method is deprecated, please use getTcpOptionEnumType() */ + PCPP_DEPRECATED("Use getTcpOptionEnumType instead") TcpOptionType getTcpOptionType() const { return getTcpOptionType(m_Data); } + /** + * @return TCP option type casted as pcpp::TcpOptionEnumType enum. If the data is null a value + * of TcpOptionEnumType::Unknown is returned + */ + TcpOptionEnumType getTcpOptionEnumType() const + { + return getTcpOptionEnumType(m_Data); + } + /** * Check if a pointer can be assigned to the TLV record data * @param[in] recordRawData A pointer to the TLV record raw data @@ -234,8 +304,8 @@ namespace pcpp if (tlvDataLen < sizeof(TLVRawData::recordType)) return false; - const auto recordType = getTcpOptionType(data); - if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL) + const auto recordType = getTcpOptionEnumType(data); + if (recordType == TcpOptionEnumType::Nop || recordType == TcpOptionEnumType::Eol) return true; return TLVRecord::canAssign(recordRawData, tlvDataLen); @@ -248,8 +318,8 @@ namespace pcpp if (m_Data == nullptr) return 0; - const auto recordType = getTcpOptionType(m_Data); - if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL) + const auto recordType = getTcpOptionEnumType(m_Data); + if (recordType == TcpOptionEnumType::Nop || recordType == TcpOptionEnumType::Eol) return sizeof(uint8_t); return static_cast(m_Data->recordLen); @@ -260,8 +330,8 @@ namespace pcpp if (m_Data == nullptr) return 0; - const auto recordType = getTcpOptionType(m_Data); - if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL) + const auto recordType = getTcpOptionEnumType(m_Data); + if (recordType == TcpOptionEnumType::Nop || recordType == TcpOptionEnumType::Eol) return 0; return static_cast(m_Data->recordLen) - (2*sizeof(uint8_t)); @@ -275,6 +345,14 @@ namespace pcpp return static_cast(optionRawData->recordType); } + + static TcpOptionEnumType getTcpOptionEnumType(const TLVRawData* optionRawData) + { + if (optionRawData == nullptr) + return TcpOptionEnumType::Unknown; + + return static_cast(optionRawData->recordType); + } }; @@ -299,6 +377,24 @@ namespace pcpp EOL }; + /** + * An enum to describe NOP and EOL TCP options. Used in one of this class's c'tors + */ + enum class NopEolOptionEnumType : uint8_t + { + /** NOP TCP option */ + Nop, + /** EOL TCP option */ + Eol + }; + + /** + * @deprecated This method is deprecated, please use constructor with TcpOptionEnumType + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : + TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + /** * A c'tor for building TCP options which their value is a byte array. The TcpOption object can be later * retrieved by calling build() @@ -306,8 +402,16 @@ namespace pcpp * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. * @param[in] optionValueLen Option value length in bytes */ - TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + TcpOptionBuilder(TcpOptionEnumType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : + TLVRecordBuilder(static_cast(optionType), optionValue, optionValueLen) {} + + /** + * @deprecated This method is deprecated, please use constructor with TcpOptionEnumType + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) {} + /** * A c'tor for building TCP options which have a 1-byte value. The TcpOption object can be later retrieved @@ -315,7 +419,14 @@ namespace pcpp * @param[in] optionType TCP option type * @param[in] optionValue A 1-byte option value */ - TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) : + TcpOptionBuilder(TcpOptionEnumType optionType, uint8_t optionValue) : + TLVRecordBuilder(static_cast(optionType), optionValue) {} + + /** + * @deprecated This method is deprecated, please use constructor with TcpOptionEnumType + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) : TLVRecordBuilder((uint8_t)optionType, optionValue) {} /** @@ -324,7 +435,14 @@ namespace pcpp * @param[in] optionType TCP option type * @param[in] optionValue A 2-byte option value */ - TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) : + TcpOptionBuilder(TcpOptionEnumType optionType, uint16_t optionValue) : + TLVRecordBuilder(static_cast(optionType), optionValue) {} + + /** + * @deprecated This method is deprecated, please use constructor with TcpOptionEnumType + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) : TLVRecordBuilder((uint8_t)optionType, optionValue) {} /** @@ -333,8 +451,14 @@ namespace pcpp * @param[in] optionType TCP option type * @param[in] optionValue A 4-byte option value */ - TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} + TcpOptionBuilder(TcpOptionEnumType optionType, uint32_t optionValue) : + TLVRecordBuilder(static_cast(optionType), optionValue) {} + + /** + * @deprecated This method is deprecated, please use constructor with NopEolOptionEnumType + */ + PCPP_DEPRECATED("enum NopEolOptionTypes is deprecated; Use enum class NopEolOptionEnumType instead") + explicit TcpOptionBuilder(NopEolOptionTypes optionType); /** * A c'tor for building TCP NOP and EOL options. These option types are special in that they contain only 1 byte @@ -342,7 +466,7 @@ namespace pcpp * by calling build() * @param[in] optionType An enum value indicating which option type to build (NOP or EOL) */ - explicit TcpOptionBuilder(NopEolOptionTypes optionType); + explicit TcpOptionBuilder(NopEolOptionEnumType optionType); /** * Build the TcpOption object out of the parameters defined in the c'tor @@ -380,7 +504,7 @@ namespace pcpp */ TcpLayer(uint16_t portSrc, uint16_t portDst); - ~TcpLayer() {} + ~TcpLayer() = default; /** * A copy constructor that copy the entire header from the other TcpLayer (including TCP options) @@ -408,13 +532,19 @@ namespace pcpp */ uint16_t getDstPort() const; + /** + * @deprecated This method is deprecated, please use getTcpOption(TcpOptionEnumType option) + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + TcpOption getTcpOption(TcpOptionType option) const; + /** * Get a TCP option by type * @param[in] option TCP option type to retrieve * @return An TcpOption object that contains the first option that matches this type, or logical NULL * (TcpOption#isNull() == true) if no such option found */ - TcpOption getTcpOption(TcpOptionType option) const; + TcpOption getTcpOption(TcpOptionEnumType option) const; /** * @return The first TCP option in the packet. If the current layer contains no options the returned value will contain @@ -446,15 +576,27 @@ namespace pcpp TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder); /** - * Add a new TCP option after an existing one - * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be added - * @param[in] prevOptionType The TCP option which the newly added option should come after. This is an optional parameter which - * gets a default value of ::TCPOPT_Unknown if omitted, which means the new option will be added as the first option in the layer - * @return A TcpOption object containing the newly added TCP option data or logical NULL - * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be + * @deprecated This method is deprecated, please use insertTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionEnumType prevOptionType = TcpOptionEnumType::Unknown) + */ + PCPP_DEPRECATED("Use insertTcpOptionAfter instead") + TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TcpOptionType::TCPOPT_Unknown); + + /** + * Insert a new TCP option after an existing one + * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be inserted + * @param[in] prevOptionType The TCP option which the newly inserted option should come after. This is an optional parameter which + * gets a default value of TcpOptionType::Unknown if omitted, which means the new option will be inserted as the first option in the layer + * @return A TcpOption object containing the newly inserted TCP option data or logical NULL + * (TcpOption#isNull() == true) if insertion failed. In case of a failure a corresponding error message will be * printed to log */ - TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TCPOPT_Unknown); + TcpOption insertTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionEnumType prevOptionType = TcpOptionEnumType::Unknown); + + /** + * @deprecated This method is deprecated, please use removeTcpOption(TcpOptionEnumType) + */ + PCPP_DEPRECATED_TCP_OPTION_TYPE + bool removeTcpOption(TcpOptionType optionType); /** * Remove an existing TCP option from the layer. TCP option is found by type @@ -462,7 +604,7 @@ namespace pcpp * @return True if TCP option was removed or false if type wasn't found or if removal failed (in each case a proper error * will be written to log) */ - bool removeTcpOption(TcpOptionType optionType); + bool removeTcpOption(TcpOptionEnumType optionType); /** * Remove all TCP options in this layer @@ -531,3 +673,5 @@ namespace pcpp && dataLen >= hdr->dataOffset * sizeof(uint32_t); } } // namespace pcpp + +#undef PCPP_DEPRECATED_TCP_OPTION_TYPE diff --git a/Packet++/src/Asn1Codec.cpp b/Packet++/src/Asn1Codec.cpp new file mode 100644 index 0000000000..e1c07d61b3 --- /dev/null +++ b/Packet++/src/Asn1Codec.cpp @@ -0,0 +1,760 @@ +#define LOG_MODULE PacketLogModuleAsn1Codec + +#include "Asn1Codec.h" +#include "GeneralUtils.h" +#include "EndianPortable.h" +#include "GeneralUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#undef max +#endif + +namespace pcpp +{ + const std::unordered_map> Asn1TagClassToString { + {Asn1TagClass::Universal, "Universal" }, + {Asn1TagClass::ContextSpecific, "ContextSpecific" }, + {Asn1TagClass::Application, "Application"}, + {Asn1TagClass::Private, "Private"} + }; + + std::string toString(Asn1TagClass tagClass) + { + if (Asn1TagClassToString.find(tagClass) != Asn1TagClassToString.end()) + { + return Asn1TagClassToString.at(tagClass); + } + + return "Unknown"; + } + + const std::unordered_map> Asn1UniversalTagTypeToString { + {Asn1UniversalTagType::EndOfContent, "EndOfContent"}, + {Asn1UniversalTagType::Boolean, "Boolean"}, + {Asn1UniversalTagType::Integer, "Integer"}, + {Asn1UniversalTagType::BitString, "BitString"}, + {Asn1UniversalTagType::OctetString, "OctetString"}, + {Asn1UniversalTagType::Null, "Null"}, + {Asn1UniversalTagType::ObjectIdentifier, "ObjectIdentifier"}, + {Asn1UniversalTagType::ObjectDescriptor, "ObjectDescriptor"}, + {Asn1UniversalTagType::External, "External"}, + {Asn1UniversalTagType::Real, "Real"}, + {Asn1UniversalTagType::Enumerated, "Enumerated"}, + {Asn1UniversalTagType::EmbeddedPDV, "EmbeddedPDV"}, + {Asn1UniversalTagType::UTF8String, "UTF8String"}, + {Asn1UniversalTagType::RelativeObjectIdentifier, "RelativeObjectIdentifier"}, + {Asn1UniversalTagType::Time, "Time"}, + {Asn1UniversalTagType::Reserved, "Reserved"}, + {Asn1UniversalTagType::Sequence, "Sequence"}, + {Asn1UniversalTagType::Set, "Set"}, + {Asn1UniversalTagType::NumericString, "NumericString"}, + {Asn1UniversalTagType::PrintableString, "PrintableString"}, + {Asn1UniversalTagType::T61String, "T61String"}, + {Asn1UniversalTagType::VideotexString, "VideotexString"}, + {Asn1UniversalTagType::IA5String, "IA5String"}, + {Asn1UniversalTagType::UTCTime, "UTCTime"}, + {Asn1UniversalTagType::GeneralizedTime, "GeneralizedTime"}, + {Asn1UniversalTagType::GraphicString, "GraphicString"}, + {Asn1UniversalTagType::VisibleString, "VisibleString"}, + {Asn1UniversalTagType::GeneralString, "GeneralString"}, + {Asn1UniversalTagType::UniversalString, "UniversalString"}, + {Asn1UniversalTagType::CharacterString, "CharacterString"}, + {Asn1UniversalTagType::BMPString, "BMPString"}, + {Asn1UniversalTagType::Date, "Date"}, + {Asn1UniversalTagType::TimeOfDay, "TimeOfDay"}, + {Asn1UniversalTagType::DateTime, "DateTime"}, + {Asn1UniversalTagType::Duration, "Duration"}, + {Asn1UniversalTagType::ObjectIdentifierIRI, "ObjectIdentifierIRI"}, + {Asn1UniversalTagType::RelativeObjectIdentifierIRI, "RelativeObjectIdentifierIRI"}, + {Asn1UniversalTagType::NotApplicable, "Unknown"} + }; + + std::string toString(Asn1UniversalTagType tagType) + { + if (Asn1UniversalTagTypeToString.find(tagType) != Asn1UniversalTagTypeToString.end()) + { + return Asn1UniversalTagTypeToString.at(tagType); + } + + return "Unknown"; + } + + std::unique_ptr Asn1Record::decode(const uint8_t* data, size_t dataLen, bool lazy) + { + auto record = decodeInternal(data, dataLen ,lazy); + return std::unique_ptr(record); + } + + uint8_t Asn1Record::encodeTag() + { + uint8_t tagByte; + + switch (m_TagClass) + { + case Asn1TagClass::Private: + { + tagByte = 0xc0; + break; + } + case Asn1TagClass::ContextSpecific: + { + tagByte = 0x80; + break; + } + case Asn1TagClass::Application: + { + tagByte = 0x40; + break; + } + default: + { + tagByte = 0; + break; + } + } + + if (m_IsConstructed) + { + tagByte |= 0x20; + } + + auto tagType = m_TagType & 0x1f; + tagByte |= tagType; + + return tagByte; + } + + std::vector Asn1Record::encodeLength() const + { + std::vector result; + + if (m_ValueLength < 128) + { + result.push_back(static_cast(m_ValueLength)); + return result; + } + + auto tempValueLength = m_ValueLength; + do + { + uint8_t byte = tempValueLength & 0xff; + result.push_back(byte); // Inserts the bytes in reverse order + tempValueLength >>= 8; + } while (tempValueLength != 0); + + uint8_t firstByte = 0x80 | static_cast(result.size()); + result.push_back(firstByte); + + // Reverses the bytes to get forward ordering + std::reverse(result.begin(), result.end()); + + return result; + } + + std::vector Asn1Record::encode() + { + std::vector result; + + result.push_back(encodeTag()); + + auto lengthBytes = encodeLength(); + result.insert(result.end(), lengthBytes.begin(), lengthBytes.end()); + + auto encodedValue = encodeValue(); + result.insert(result.end(), encodedValue.begin(), encodedValue.end()); + + return result; + } + + Asn1Record* Asn1Record::decodeInternal(const uint8_t* data, size_t dataLen, bool lazy) + { + int tagLen; + auto decodedRecord = decodeTagAndCreateRecord(data, dataLen, tagLen); + + int lengthLen; + try + { + lengthLen = decodedRecord->decodeLength(data + tagLen, dataLen - tagLen); + } + catch (...) + { + delete decodedRecord; + throw; + } + + if (static_cast(dataLen) - tagLen - lengthLen - static_cast(decodedRecord->m_ValueLength) < 0) + { + delete decodedRecord; + throw std::invalid_argument("Cannot decode ASN.1 record, data doesn't contain the entire record"); + } + + decodedRecord->m_TotalLength = tagLen + lengthLen + decodedRecord->m_ValueLength; + + if (!lazy) + { + try + { + decodedRecord->decodeValue(const_cast(data) + tagLen + lengthLen, lazy); + } + catch (...) + { + delete decodedRecord; + throw; + } + + } + else + { + decodedRecord->m_EncodedValue = const_cast(data) + tagLen + lengthLen; + } + + return decodedRecord; + } + + Asn1UniversalTagType Asn1Record::getUniversalTagType() const + { + if (m_TagClass == Asn1TagClass::Universal) + { + return static_cast(m_TagType); + } + + return Asn1UniversalTagType::NotApplicable; + } + + Asn1Record* Asn1Record::decodeTagAndCreateRecord(const uint8_t* data, size_t dataLen, int& tagLen) + { + if (dataLen < 1) + { + throw std::invalid_argument("Cannot decode ASN.1 record tag"); + } + + tagLen = 1; + + Asn1TagClass tagClass = Asn1TagClass::Universal; + + // Check first 2 bits + auto tagClassBits = data[0] & 0xc0; + if (tagClassBits == 0) + { + tagClass = Asn1TagClass::Universal; + } + else if ((tagClassBits & 0xc0) == 0xc0) + { + tagClass = Asn1TagClass::Private; + } + else if ((tagClassBits & 0x80) == 0x80) + { + tagClass = Asn1TagClass::ContextSpecific; + } + else if ((tagClassBits & 0x40) == 0x40) + { + tagClass = Asn1TagClass::Application; + } + + // Check bit 6 + auto tagTypeBits = data[0] & 0x20; + bool isConstructed = (tagTypeBits != 0); + + // Check last 5 bits + auto tagType = data[0] & 0x1f; + if (tagType == 0x1f) + { + if (dataLen < 2) + { + throw std::invalid_argument("Cannot decode ASN.1 record tag"); + } + + if ((data[1] & 0x80) != 0) + { + throw std::invalid_argument("ASN.1 tags with value larger than 127 are not supported"); + } + + tagType = data[1] & 0x7f; + tagLen = 2; + } + + Asn1Record* newRecord; + + if (isConstructed) + { + if (tagClass == Asn1TagClass::Universal) + { + switch (static_cast(tagType)) + { + case Asn1UniversalTagType::Sequence: + { + newRecord = new Asn1SequenceRecord(); + break; + } + case Asn1UniversalTagType::Set: + { + newRecord = new Asn1SetRecord(); + break; + } + default: + { + newRecord = new Asn1ConstructedRecord(); + } + } + } + else + { + newRecord = new Asn1ConstructedRecord(); + } + } + else + { + if (tagClass == Asn1TagClass::Universal) + { + auto asn1UniversalTagType = static_cast(tagType); + switch (asn1UniversalTagType) + { + case Asn1UniversalTagType::Integer: + { + newRecord = new Asn1IntegerRecord(); + break; + } + case Asn1UniversalTagType::Enumerated: + { + newRecord = new Asn1EnumeratedRecord(); + break; + } + case Asn1UniversalTagType::OctetString: + { + newRecord = new Asn1OctetStringRecord(); + break; + } + case Asn1UniversalTagType::Boolean: + { + newRecord = new Asn1BooleanRecord(); + break; + } + case Asn1UniversalTagType::Null: + { + newRecord = new Asn1NullRecord(); + break; + } + default: + { + newRecord = new Asn1GenericRecord(); + } + } + } + else + { + newRecord = new Asn1GenericRecord(); + } + } + + newRecord->m_TagClass = tagClass; + newRecord->m_IsConstructed = isConstructed; + newRecord->m_TagType = tagType; + + return newRecord; + } + + int Asn1Record::decodeLength(const uint8_t* data, size_t dataLen) + { + if (dataLen < 1) + { + throw std::invalid_argument("Cannot decode ASN.1 record length"); + } + + // Check 8th bit + auto lengthForm = data[0] & 0x80; + + auto numberLengthBytes = 1; + + // Check if the tag is using more than one byte + // 8th bit at 0 means the length only uses one byte + // 8th bit at 1 means the length uses more than one byte. The number of bytes is encoded in the other 7 bits + if (lengthForm != 0) + { + auto additionalLengthBytes = data[0] & 0x7F; + if (static_cast(dataLen) < additionalLengthBytes + 1) + { + throw std::invalid_argument("Cannot decode ASN.1 record length"); + } + for (auto index = additionalLengthBytes; index > 0; --index) + { + m_ValueLength += data[index] * static_cast(std::pow(256, (additionalLengthBytes - index))); + } + numberLengthBytes += additionalLengthBytes; + } + else + { + m_ValueLength = data[0]; + } + + return numberLengthBytes; + } + + void Asn1Record::decodeValueIfNeeded() + { + if (m_EncodedValue != nullptr) + { + decodeValue(m_EncodedValue, true); + m_EncodedValue = nullptr; + } + } + + std::string Asn1Record::toString() + { + auto lines = toStringList(); + + auto commaSeparated = [](std::string str1, std::string str2) + { + return std::move(str1) + '\n' + std::move(str2); + }; + + return std::accumulate(std::next(lines.begin()), lines.end(), lines[0], commaSeparated); + } + + std::vector Asn1Record::toStringList() + { + std::ostringstream stream; + + auto universalType = getUniversalTagType(); + if (universalType == Asn1UniversalTagType::NotApplicable) + { + stream << pcpp::toString(m_TagClass) << " (" << static_cast(m_TagType) << ")"; + } + else + { + stream << pcpp::toString(universalType); + } + + if (m_IsConstructed) + { + stream << " (constructed)"; + } + + stream << ", Length: " << m_TotalLength - m_ValueLength << "+" << m_ValueLength; + + return {stream.str()}; + } + + Asn1GenericRecord::Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value, size_t valueLen) + { + init(tagClass, isConstructed, tagType, value, valueLen); + } + + Asn1GenericRecord::Asn1GenericRecord(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const std::string& value) + { + init(tagClass, isConstructed, tagType, reinterpret_cast(value.c_str()), value.size()); + } + + Asn1GenericRecord::~Asn1GenericRecord() + { + delete m_Value; + } + + void Asn1GenericRecord::decodeValue(uint8_t* data, bool lazy) + { + delete m_Value; + + m_Value = new uint8_t[m_ValueLength]; + memcpy(m_Value, data, m_ValueLength); + } + + std::vector Asn1GenericRecord::encodeValue() const + { + return {m_Value, m_Value + m_ValueLength}; + } + + void Asn1GenericRecord::init(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value, size_t valueLen) + { + m_TagType = tagType; + m_TagClass = tagClass; + m_IsConstructed = isConstructed; + m_Value = new uint8_t[valueLen]; + memcpy(m_Value, value, valueLen); + m_ValueLength = valueLen; + m_TotalLength = m_ValueLength + 2; + } + + Asn1ConstructedRecord::Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType, const std::vector& subRecords) + { + init(tagClass, tagType, subRecords.begin(), subRecords.end()); + } + + Asn1ConstructedRecord::Asn1ConstructedRecord(Asn1TagClass tagClass, uint8_t tagType, const PointerVector& subRecords) + { + init(tagClass, tagType, subRecords.begin(), subRecords.end()); + } + + void Asn1ConstructedRecord::decodeValue(uint8_t* data, bool lazy) + { + if (!(data || m_ValueLength)) + { + return; + } + + auto value = data; + auto valueLen = m_ValueLength; + + while (valueLen > 0) + { + auto subRecord = Asn1Record::decodeInternal(value, valueLen, lazy); + value += subRecord->getTotalLength(); + valueLen -= subRecord->getTotalLength(); + + m_SubRecords.pushBack(subRecord); + } + } + + std::vector Asn1ConstructedRecord::encodeValue() const + { + std::vector result; + result.reserve(m_ValueLength); + + for (auto record : m_SubRecords) + { + auto encodedRecord = record->encode(); + result.insert(result.end(), std::make_move_iterator(encodedRecord.begin()), std::make_move_iterator(encodedRecord.end())); + } + return result; + } + + std::vector Asn1ConstructedRecord::toStringList() + { + decodeValueIfNeeded(); + std::vector result = {Asn1Record::toStringList().front()}; + for (auto subRecord : m_SubRecords) + { + for (const auto& line : subRecord->toStringList()) + { + result.push_back(" " + line); + } + } + return result; + } + + Asn1SequenceRecord::Asn1SequenceRecord(const std::vector& subRecords) + : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast(Asn1UniversalTagType::Sequence), subRecords) + {} + + Asn1SequenceRecord::Asn1SequenceRecord(const PointerVector& subRecords) + : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast(Asn1UniversalTagType::Sequence), subRecords) + {} + + Asn1SetRecord::Asn1SetRecord(const std::vector& subRecords) + : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast(Asn1UniversalTagType::Set), subRecords) + {} + + Asn1SetRecord::Asn1SetRecord(const PointerVector& subRecords) + : Asn1ConstructedRecord(Asn1TagClass::Universal, static_cast(Asn1UniversalTagType::Set), subRecords) + {} + + Asn1PrimitiveRecord::Asn1PrimitiveRecord(Asn1UniversalTagType tagType) : Asn1Record() + { + m_TagType = static_cast(tagType); + m_TagClass = Asn1TagClass::Universal; + m_IsConstructed = false; + } + + Asn1IntegerRecord::Asn1IntegerRecord(uint32_t value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Integer) + { + m_Value = value; + + if (m_Value <= std::numeric_limits::max()) + { + m_ValueLength = sizeof(uint8_t); + } + else if (value <= std::numeric_limits::max()) + { + m_ValueLength = sizeof(uint16_t); + } + else if (value <= std::pow(2, 3 * 8)) + { + m_ValueLength = 3; + } + else + { + m_ValueLength = sizeof(uint32_t); + } + + m_TotalLength = m_ValueLength + 2; + } + + void Asn1IntegerRecord::decodeValue(uint8_t* data, bool lazy) + { + switch (m_ValueLength) + { + case 1: + { + m_Value = *data; + break; + } + case 2: + { + m_Value = be16toh(*reinterpret_cast(data)); + break; + } + case 3: + { + uint8_t tempData[4] = {0}; + memcpy(tempData + 1, data, 3); + m_Value = be32toh(*reinterpret_cast(tempData)); + break; + } + case 4: + { + m_Value = be32toh(*reinterpret_cast(data)); + break; + } + default: + { + throw std::runtime_error("An integer ASN.1 record of more than 4 bytes is not supported"); + } + } + } + + std::vector Asn1IntegerRecord::encodeValue() const + { + std::vector result; + result.reserve(m_ValueLength); + + switch (m_ValueLength) + { + case 1: + { + result.push_back(static_cast(m_Value)); + break; + } + case 2: + { + uint8_t tempArr[sizeof(uint16_t)]; + auto hostValue = htobe16(static_cast(m_Value)); + memcpy(tempArr, &hostValue, m_ValueLength); + std::copy(tempArr, tempArr + m_ValueLength, std::back_inserter(result)); + break; + } + case 3: + { + uint8_t tempArr[sizeof(uint32_t)]; + auto hostValue = htobe32(static_cast(m_Value)); + memcpy(tempArr, &hostValue, m_ValueLength + 1); + std::copy(tempArr + 1, tempArr + m_ValueLength + 1, std::back_inserter(result)); + break; + } + case 4: + { + uint8_t tempArr[sizeof(uint32_t)]; + auto hostValue = htobe32(static_cast(m_Value)); + memcpy(tempArr, &hostValue, m_ValueLength); + std::copy(tempArr, tempArr + m_ValueLength, std::back_inserter(result)); + break; + } + default: + { + throw std::runtime_error("Integer value of more than 4 bytes is not supported"); + } + } + + return result; + } + + std::vector Asn1IntegerRecord::toStringList() + { + return std::vector({Asn1Record::toStringList().front() + ", Value: " + std::to_string(getValue())}); + } + + Asn1EnumeratedRecord::Asn1EnumeratedRecord(uint32_t value) : Asn1IntegerRecord(value) + { + m_TagType = static_cast(Asn1UniversalTagType::Enumerated); + } + + Asn1OctetStringRecord::Asn1OctetStringRecord(const std::string& value) : Asn1PrimitiveRecord(Asn1UniversalTagType::OctetString) + { + m_Value = value; + m_ValueLength = value.size(); + m_TotalLength = m_ValueLength + 2; + m_IsPrintable = true; + } + + Asn1OctetStringRecord::Asn1OctetStringRecord(const uint8_t* value, size_t valueLength) : Asn1PrimitiveRecord(Asn1UniversalTagType::OctetString) + { + m_Value = byteArrayToHexString(value, valueLength); + m_ValueLength = valueLength; + m_TotalLength = m_ValueLength + 2; + m_IsPrintable = false; + } + + void Asn1OctetStringRecord::decodeValue(uint8_t* data, bool lazy) + { + auto value = reinterpret_cast(data); + + m_IsPrintable = std::all_of(value, value + m_ValueLength, [](char c) { + return isprint(0xff & c); + }); + + if (m_IsPrintable) + { + m_Value = std::string(value, m_ValueLength); + } + else + { + m_Value = byteArrayToHexString(data, m_ValueLength); + } + } + + std::vector Asn1OctetStringRecord::encodeValue() const + { + if (m_IsPrintable) + { + return {m_Value.begin(), m_Value.end()}; + } + + // converting the hex stream to a byte array. + // The byte array size is half the size of the string + // i.e "1a2b" (length == 4) becomes {0x1a, 0x2b} (length == 2) + auto rawValueSize = static_cast(m_Value.size() / 2); + std::vector rawValue; + rawValue.resize(rawValueSize); + hexStringToByteArray(m_Value, rawValue.data(), rawValueSize); + return rawValue; + } + + std::vector Asn1OctetStringRecord::toStringList() + { + return {Asn1Record::toStringList().front() + ", Value: " + getValue()}; + } + + Asn1BooleanRecord::Asn1BooleanRecord(bool value) : Asn1PrimitiveRecord(Asn1UniversalTagType::Boolean) + { + m_Value = value; + m_ValueLength = 1; + m_TotalLength = 3; + } + + void Asn1BooleanRecord::decodeValue(uint8_t* data, bool lazy) + { + m_Value = data[0] != 0; + } + + std::vector Asn1BooleanRecord::encodeValue() const + { + uint8_t byte = (m_Value ? 0xff : 0x00); + return { byte }; + } + + std::vector Asn1BooleanRecord::toStringList() + { + return {Asn1Record::toStringList().front() + ", Value: " + (getValue() ? "true" : "false")}; + } + + Asn1NullRecord::Asn1NullRecord() : Asn1PrimitiveRecord(Asn1UniversalTagType::Null) + { + m_ValueLength = 0; + m_TotalLength = 2; + } +} diff --git a/Packet++/src/DnsResource.cpp b/Packet++/src/DnsResource.cpp index fec1bf0597..c1f97041a2 100644 --- a/Packet++/src/DnsResource.cpp +++ b/Packet++/src/DnsResource.cpp @@ -10,7 +10,7 @@ namespace pcpp { IDnsResource::IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer) - : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr) + : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr), m_ExternalRawData(nullptr) { char decodedName[4096]; m_NameLength = decodeName((const char*)getRawData(), decodedName); diff --git a/Packet++/src/IPv6Layer.cpp b/Packet++/src/IPv6Layer.cpp index 72d97657a8..f6b5d78546 100644 --- a/Packet++/src/IPv6Layer.cpp +++ b/Packet++/src/IPv6Layer.cpp @@ -187,8 +187,7 @@ void IPv6Layer::removeAllExtensions() bool IPv6Layer::isFragment() const { - IPv6FragmentationHeader* fragHdr = getExtensionOfType(); - return (fragHdr != nullptr); + return getExtensionOfType() != nullptr; } void IPv6Layer::parseNextLayer() diff --git a/Packet++/src/LdapLayer.cpp b/Packet++/src/LdapLayer.cpp new file mode 100644 index 0000000000..5874c2a5e5 --- /dev/null +++ b/Packet++/src/LdapLayer.cpp @@ -0,0 +1,793 @@ +#include "LdapLayer.h" +#include "GeneralUtils.h" +#include + +namespace pcpp { + + // region LdapOperationType + + static const std::unordered_map> LdapOperationTypeToString{ + {LdapOperationType::BindRequest, "BindRequest"}, + {LdapOperationType::BindResponse, "BindResponse"}, + {LdapOperationType::UnbindRequest, "UnbindRequest"}, + {LdapOperationType::SearchRequest, "SearchRequest"}, + {LdapOperationType::SearchResultEntry, "SearchResultEntry"}, + {LdapOperationType::SearchResultDone, "SearchResultDone"}, + {LdapOperationType::ModifyRequest, "ModifyRequest"}, + {LdapOperationType::ModifyResponse, "ModifyResponse"}, + {LdapOperationType::AddRequest, "AddRequest"}, + {LdapOperationType::AddResponse, "AddResponse"}, + {LdapOperationType::DeleteRequest, "DeleteRequest"}, + {LdapOperationType::DeleteResponse, "DeleteResponse"}, + {LdapOperationType::ModifyDNRequest, "ModifyDNRequest"}, + {LdapOperationType::ModifyDNResponse, "ModifyDNResponse"}, + {LdapOperationType::CompareRequest, "CompareRequest"}, + {LdapOperationType::CompareResponse, "CompareResponse"}, + {LdapOperationType::AbandonRequest, "AbandonRequest"}, + {LdapOperationType::SearchResultReference, "SearchResultReference"}, + {LdapOperationType::ExtendedRequest, "ExtendedRequest"}, + {LdapOperationType::ExtendedResponse, "ExtendedResponse"}, + {LdapOperationType::IntermediateResponse, "IntermediateResponse"}, + {LdapOperationType::Unknown, "Unknown"} + }; + + static const std::unordered_map UintToLdapOperationType{ + {static_cast(LdapOperationType::BindRequest), LdapOperationType::BindRequest}, + {static_cast(LdapOperationType::BindResponse), LdapOperationType::BindResponse}, + {static_cast(LdapOperationType::UnbindRequest), LdapOperationType::UnbindRequest}, + {static_cast(LdapOperationType::SearchRequest), LdapOperationType::SearchRequest}, + {static_cast(LdapOperationType::SearchResultEntry), LdapOperationType::SearchResultEntry}, + {static_cast(LdapOperationType::SearchResultDone), LdapOperationType::SearchResultDone}, + {static_cast(LdapOperationType::ModifyResponse), LdapOperationType::ModifyResponse}, + {static_cast(LdapOperationType::AddRequest), LdapOperationType::AddRequest}, + {static_cast(LdapOperationType::AddResponse), LdapOperationType::AddResponse}, + {static_cast(LdapOperationType::DeleteRequest), LdapOperationType::DeleteRequest}, + {static_cast(LdapOperationType::DeleteResponse), LdapOperationType::DeleteResponse}, + {static_cast(LdapOperationType::ModifyDNRequest), LdapOperationType::ModifyDNRequest}, + {static_cast(LdapOperationType::ModifyDNResponse), LdapOperationType::ModifyDNResponse}, + {static_cast(LdapOperationType::CompareRequest), LdapOperationType::CompareRequest}, + {static_cast(LdapOperationType::CompareResponse), LdapOperationType::CompareResponse}, + {static_cast(LdapOperationType::AbandonRequest), LdapOperationType::AbandonRequest}, + {static_cast(LdapOperationType::SearchResultReference), LdapOperationType::SearchResultReference}, + {static_cast(LdapOperationType::ExtendedRequest), LdapOperationType::ExtendedRequest}, + {static_cast(LdapOperationType::ExtendedResponse), LdapOperationType::ExtendedResponse}, + {static_cast(LdapOperationType::IntermediateResponse), LdapOperationType::IntermediateResponse} + }; + + std::string LdapOperationType::toString() const + { + return LdapOperationTypeToString.at(m_Value); + } + + LdapOperationType LdapOperationType::fromUintValue(uint8_t value) + { + auto result = UintToLdapOperationType.find(value); + if (result != UintToLdapOperationType.end()) + { + return result->second; + } + + return LdapOperationType::Unknown; + } + + // endregion + + // region LdapResultCode + + static const std::unordered_map> LdapResultCodeToString{ + {LdapResultCode::Success, "Success"}, + {LdapResultCode::OperationsError, "OperationsError"}, + {LdapResultCode::ProtocolError, "ProtocolError"}, + {LdapResultCode::TimeLimitExceeded, "TimeLimitExceeded"}, + {LdapResultCode::SizeLimitExceeded, "SizeLimitExceeded"}, + {LdapResultCode::CompareFalse, "CompareFalse"}, + {LdapResultCode::CompareTrue, "CompareTrue"}, + {LdapResultCode::AuthMethodNotSupported, "AuthMethodNotSupported"}, + {LdapResultCode::StrongerAuthRequired, "StrongerAuthRequired"}, + {LdapResultCode::Referral, "Referral"}, + {LdapResultCode::AdminLimitExceeded, "AdminLimitExceeded"}, + {LdapResultCode::UnavailableCriticalExtension, "UnavailableCriticalExtension"}, + {LdapResultCode::ConfidentialityRequired, "ConfidentialityRequired"}, + {LdapResultCode::SaslBindInProgress, "SaslBindInProgress"}, + {LdapResultCode::NoSuchAttribute, "NoSuchAttribute"}, + {LdapResultCode::UndefinedAttributeType, "UndefinedAttributeType"}, + {LdapResultCode::InappropriateMatching, "InappropriateMatching"}, + {LdapResultCode::ConstraintViolation, "ConstraintViolation"}, + {LdapResultCode::AttributeOrValueExists, "AttributeOrValueExists"}, + {LdapResultCode::InvalidAttributeSyntax, "InvalidAttributeSyntax"}, + {LdapResultCode::NoSuchObject, "NoSuchObject"}, + {LdapResultCode::AliasProblem, "AliasProblem"}, + {LdapResultCode::InvalidDNSyntax, "InvalidDNSyntax"}, + {LdapResultCode::AliasDereferencingProblem, "AliasDereferencingProblem"}, + {LdapResultCode::InappropriateAuthentication, "InappropriateAuthentication"}, + {LdapResultCode::InvalidCredentials, "InvalidCredentials"}, + {LdapResultCode::InsufficientAccessRights, "InsufficientAccessRights"}, + {LdapResultCode::Busy, "Busy"}, + {LdapResultCode::Unavailable, "Unavailable"}, + {LdapResultCode::UnwillingToPerform, "UnwillingToPerform"}, + {LdapResultCode::LoopDetect, "LoopDetect"}, + {LdapResultCode::NamingViolation, "NamingViolation"}, + {LdapResultCode::ObjectClassViolation, "ObjectClassViolation"}, + {LdapResultCode::NotAllowedOnNonLeaf, "NotAllowedOnNonLeaf"}, + {LdapResultCode::NotAllowedOnRDN, "NotAllowedOnRDN"}, + {LdapResultCode::EntryAlreadyExists, "EntryAlreadyExists"}, + {LdapResultCode::ObjectClassModsProhibited, "ObjectClassModsProhibited"}, + {LdapResultCode::AffectsMultipleDSAs, "AffectsMultipleDSAs"}, + {LdapResultCode::Other, "Other"} + }; + + static const std::unordered_map UintToLdapResultCode{ + {static_cast(LdapResultCode::Success), LdapResultCode::Success}, + {static_cast(LdapResultCode::OperationsError), LdapResultCode::OperationsError}, + {static_cast(LdapResultCode::ProtocolError), LdapResultCode::ProtocolError}, + {static_cast(LdapResultCode::TimeLimitExceeded), LdapResultCode::TimeLimitExceeded}, + {static_cast(LdapResultCode::SizeLimitExceeded), LdapResultCode::SizeLimitExceeded}, + {static_cast(LdapResultCode::CompareFalse), LdapResultCode::CompareFalse}, + {static_cast(LdapResultCode::CompareTrue), LdapResultCode::CompareTrue}, + {static_cast(LdapResultCode::AuthMethodNotSupported), LdapResultCode::AuthMethodNotSupported}, + {static_cast(LdapResultCode::StrongerAuthRequired), LdapResultCode::StrongerAuthRequired}, + {static_cast(LdapResultCode::Referral), LdapResultCode::Referral}, + {static_cast(LdapResultCode::AdminLimitExceeded), LdapResultCode::AdminLimitExceeded}, + {static_cast(LdapResultCode::UnavailableCriticalExtension), LdapResultCode::UnavailableCriticalExtension}, + {static_cast(LdapResultCode::ConfidentialityRequired), LdapResultCode::ConfidentialityRequired}, + {static_cast(LdapResultCode::SaslBindInProgress), LdapResultCode::SaslBindInProgress}, + {static_cast(LdapResultCode::NoSuchAttribute), LdapResultCode::NoSuchAttribute}, + {static_cast(LdapResultCode::UndefinedAttributeType), LdapResultCode::UndefinedAttributeType}, + {static_cast(LdapResultCode::InappropriateMatching), LdapResultCode::InappropriateMatching}, + {static_cast(LdapResultCode::ConstraintViolation), LdapResultCode::ConstraintViolation}, + {static_cast(LdapResultCode::AttributeOrValueExists), LdapResultCode::AttributeOrValueExists}, + {static_cast(LdapResultCode::InvalidAttributeSyntax), LdapResultCode::InvalidAttributeSyntax}, + {static_cast(LdapResultCode::NoSuchObject), LdapResultCode::NoSuchObject}, + {static_cast(LdapResultCode::AliasProblem), LdapResultCode::AliasProblem}, + {static_cast(LdapResultCode::InvalidDNSyntax), LdapResultCode::InvalidDNSyntax}, + {static_cast(LdapResultCode::AliasDereferencingProblem), LdapResultCode::AliasDereferencingProblem}, + {static_cast(LdapResultCode::InappropriateAuthentication), LdapResultCode::InappropriateAuthentication}, + {static_cast(LdapResultCode::InvalidCredentials), LdapResultCode::InvalidCredentials}, + {static_cast(LdapResultCode::InsufficientAccessRights), LdapResultCode::InsufficientAccessRights}, + {static_cast(LdapResultCode::Busy), LdapResultCode::Busy}, + {static_cast(LdapResultCode::Unavailable), LdapResultCode::Unavailable}, + {static_cast(LdapResultCode::UnwillingToPerform), LdapResultCode::UnwillingToPerform}, + {static_cast(LdapResultCode::LoopDetect), LdapResultCode::LoopDetect}, + {static_cast(LdapResultCode::NamingViolation), LdapResultCode::NamingViolation}, + {static_cast(LdapResultCode::ObjectClassViolation), LdapResultCode::ObjectClassViolation}, + {static_cast(LdapResultCode::NotAllowedOnNonLeaf), LdapResultCode::NotAllowedOnNonLeaf}, + {static_cast(LdapResultCode::NotAllowedOnRDN), LdapResultCode::NotAllowedOnRDN}, + {static_cast(LdapResultCode::EntryAlreadyExists), LdapResultCode::EntryAlreadyExists}, + {static_cast(LdapResultCode::ObjectClassModsProhibited), LdapResultCode::ObjectClassModsProhibited}, + {static_cast(LdapResultCode::AffectsMultipleDSAs), LdapResultCode::AffectsMultipleDSAs}, + {static_cast(LdapResultCode::Other), LdapResultCode::Other} + }; + + std::string LdapResultCode::toString() const + { + return LdapResultCodeToString.at(m_Value); + } + + LdapResultCode LdapResultCode::fromUintValue(uint8_t value) + { + auto result = UintToLdapResultCode.find(value); + if (result != UintToLdapResultCode.end()) + { + return result->second; + } + + return LdapResultCode::Unknown; + } + + // endregion + + // region LdapLayer + + LdapLayer::LdapLayer(uint16_t messageId, LdapOperationType operationType, + const std::vector& messageRecords, const std::vector& controls) + { + init(messageId, operationType, messageRecords, controls); + } + + LdapLayer::LdapLayer(std::unique_ptr asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) + { + m_Protocol = LDAP; + m_Asn1Record = std::move(asn1Record); + } + + void LdapLayer::init(uint16_t messageId, LdapOperationType operationType, const std::vector& messageRecords, const std::vector& controls) + { + Asn1IntegerRecord messageIdRecord(messageId); + std::unique_ptr messageRootRecord; + if (!messageRecords.empty()) + { + messageRootRecord = std::unique_ptr(new Asn1ConstructedRecord(Asn1TagClass::Application, operationType, messageRecords)); + } + else + { + messageRootRecord = std::unique_ptr(new Asn1GenericRecord(Asn1TagClass::Application, false, operationType, "")); + } + + std::vector rootSubRecords = {&messageIdRecord, messageRootRecord.get()}; + + std::unique_ptr controlsRecord; + if (!controls.empty()) + { + PointerVector controlsSubRecords; + for (const auto& control : controls) + { + Asn1OctetStringRecord controlTypeRecord(control.controlType); + if (control.controlValue.empty()) + { + controlsSubRecords.pushBack(new Asn1SequenceRecord({&controlTypeRecord})); + } + else + { + auto controlValueSize = static_cast(control.controlValue.size() / 2); + std::unique_ptr controlValue(new uint8_t[controlValueSize]); + controlValueSize = hexStringToByteArray(control.controlValue, controlValue.get(), controlValueSize); + Asn1OctetStringRecord controlValueRecord(controlValue.get(), controlValueSize); + controlsSubRecords.pushBack(new Asn1SequenceRecord({&controlTypeRecord, &controlValueRecord})); + } + } + controlsRecord = std::unique_ptr(new Asn1ConstructedRecord(Asn1TagClass::ContextSpecific, 0, controlsSubRecords)); + rootSubRecords.push_back(controlsRecord.get()); + } + + Asn1SequenceRecord rootRecord(rootSubRecords); + + auto encodedData = rootRecord.encode(); + m_DataLen = encodedData.size(); + m_Data = new uint8_t[m_DataLen]; + std::copy(encodedData.begin(), encodedData.end(), m_Data); + m_Protocol = LDAP; + m_Asn1Record = Asn1Record::decode(m_Data, m_DataLen, true); + } + + std::string LdapLayer::toString() const + { + auto extendedInfo = getExtendedInfoString(); + return "LDAP Layer, " + getLdapOperationType().toString() + (extendedInfo.empty() ? "" : ", " + extendedInfo); + } + + LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + { + try + { + auto asn1Record = Asn1Record::decode(data, dataLen, true); + auto operationType = LdapOperationType::fromUintValue(asn1Record->castAs()->getSubRecords().at(operationTypeIndex)->getTagType()); + switch (operationType) + { + case LdapOperationType::BindRequest: + return new LdapBindRequestLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::BindResponse: + return new LdapBindResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::UnbindRequest: + return new LdapUnbindRequestLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::SearchRequest: + return new LdapSearchRequestLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::SearchResultEntry: + return new LdapSearchResultEntryLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::SearchResultDone: + return new LdapSearchResultDoneLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::ModifyResponse: + return new LdapModifyResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::AddResponse: + return new LdapAddResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::DeleteResponse: + return new LdapDeleteResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::ModifyDNResponse: + return new LdapModifyDNResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::CompareResponse: + return new LdapCompareResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + case LdapOperationType::Unknown: + return nullptr; + default: + return new LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet); + } + } + catch (...) + { + return nullptr; + } + } + + Asn1SequenceRecord* LdapLayer::getRootAsn1Record() const + { + return m_Asn1Record->castAs(); + } + + Asn1ConstructedRecord* LdapLayer::getLdapOperationAsn1Record() const + { + return getRootAsn1Record()->getSubRecords().at(operationTypeIndex)->castAs(); + } + + uint16_t LdapLayer::getMessageID() const + { + return getRootAsn1Record()->getSubRecords().at(messageIdIndex)->castAs()->getValue(); + } + + std::vector LdapLayer::getControls() const + { + std::vector controls; + if (getRootAsn1Record()->getSubRecords().size() <= controlsIndex) + { + return controls; + } + + auto controlsRecord = getRootAsn1Record()->getSubRecords().at(controlsIndex)->castAs(); + for (auto controlRecord : controlsRecord->getSubRecords()) + { + auto controlSequence = controlRecord->castAs(); + auto controlType = controlSequence->getSubRecords().at(controlTypeIndex)->castAs()->getValue(); + std::string controlValue; + if (controlSequence->getSubRecords().size() > controlValueIndex) + { + controlValue = controlSequence->getSubRecords().at(controlValueIndex)->castAs()->getValue(); + } + controls.push_back({ controlType, controlValue }); + } + + return controls; + } + + LdapOperationType LdapLayer::getLdapOperationType() const + { + return LdapOperationType::fromUintValue(getLdapOperationAsn1Record()->getTagType()); + } + + void LdapLayer::parseNextLayer() + { + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen || headerLen == 0) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + m_NextLayer = LdapLayer::parseLdapMessage(payload, payloadLen, this, m_Packet); + } + // endregion + + // region LdapResponseLayer + + LdapResponseLayer::LdapResponseLayer(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode, + const std::string& matchedDN, const std::string& diagnosticMessage, const std::vector& referral, + const std::vector& controls) + { + LdapResponseLayer::init(messageId, operationType, resultCode, matchedDN, diagnosticMessage, referral, {}, controls); + } + + void LdapResponseLayer::init(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode, + const std::string& matchedDN, const std::string& diagnosticMessage, const std::vector& referral, + const std::vector& additionalRecords, const std::vector& controls) + { + Asn1EnumeratedRecord resultCodeRecord(resultCode); + Asn1OctetStringRecord matchedDNRecord(matchedDN); + Asn1OctetStringRecord diagnosticMessageRecord(diagnosticMessage); + + std::vector messageRecords = {&resultCodeRecord, &matchedDNRecord, &diagnosticMessageRecord}; + + std::unique_ptr referralRecord; + if (!referral.empty()) + { + PointerVector referralSubRecords; + for (const auto& uri : referral) + { + referralSubRecords.pushBack(new Asn1OctetStringRecord(uri)); + } + referralRecord = std::unique_ptr(new Asn1ConstructedRecord( + Asn1TagClass::ContextSpecific, referralTagType, referralSubRecords)); + messageRecords.push_back(referralRecord.get()); + } + + if (!additionalRecords.empty()) + { + for (auto additionalRecord : additionalRecords) + { + messageRecords.push_back(additionalRecord); + } + } + + LdapLayer::init(messageId, operationType, messageRecords, controls); + } + + LdapResultCode LdapResponseLayer::getResultCode() const + { + return LdapResultCode::fromUintValue(getLdapOperationAsn1Record()->getSubRecords().at(resultCodeIndex)->castAs()->getValue()); + } + + std::string LdapResponseLayer::getMatchedDN() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(matchedDNIndex)->castAs()->getValue(); + } + + std::string LdapResponseLayer::getDiagnosticMessage() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(diagnotsticsMessageIndex)->castAs()->getValue(); + } + + std::vector LdapResponseLayer::getReferral() const + { + std::vector result; + if (getLdapOperationAsn1Record()->getSubRecords().size() <= referralIndex) + { + return result; + } + + auto referralRecord = getLdapOperationAsn1Record()->getSubRecords().at(referralIndex); + if (referralRecord->getTagClass() != Asn1TagClass::ContextSpecific || referralRecord->getTagType() != referralTagType) + { + return result; + } + + for (auto uriRecord : referralRecord->castAs()->getSubRecords()) + { + result.push_back(uriRecord->castAs()->getValue()); + } + + return result; + } + + std::string LdapResponseLayer::getExtendedInfoString() const + { + return getResultCode().toString(); + } + // endregion + + // region LdapBindRequestLayer + + LdapBindRequestLayer::LdapBindRequestLayer( + uint16_t messageId, uint8_t version, const std::string& name, const std::string& simpleAuthentication, + const std::vector& controls) + { + Asn1IntegerRecord versionRecord(version); + Asn1OctetStringRecord nameRecord(name); + std::vector messageRecords = {&versionRecord, &nameRecord}; + std::unique_ptr simpleAuthenticationRecord; + if (!simpleAuthentication.empty()) + { + auto data = reinterpret_cast(simpleAuthentication.data()); + simpleAuthenticationRecord = std::unique_ptr( + new Asn1GenericRecord(Asn1TagClass::ContextSpecific, false, static_cast(LdapBindRequestLayer::AuthenticationType::Simple), data, simpleAuthentication.size())); + messageRecords.push_back(simpleAuthenticationRecord.get()); + } + + LdapLayer::init(messageId, LdapOperationType::BindRequest, messageRecords, controls); + } + + LdapBindRequestLayer::LdapBindRequestLayer( + uint16_t messageId, uint8_t version, const std::string& name, const SaslAuthentication& saslAuthentication, + const std::vector& controls) + { + Asn1IntegerRecord versionRecord(version); + Asn1OctetStringRecord nameRecord(name); + std::vector messageRecords = {&versionRecord, &nameRecord}; + std::unique_ptr saslAuthenticationRecord; + if (!saslAuthentication.mechanism.empty()) + { + PointerVector saslAuthenticationRecords; + saslAuthenticationRecords.pushBack(new Asn1OctetStringRecord(saslAuthentication.mechanism)); + if (!saslAuthentication.credentials.empty()) + { + auto credentialsRecord = new Asn1OctetStringRecord(saslAuthentication.credentials.data(), saslAuthentication.credentials.size()); + saslAuthenticationRecords.pushBack(credentialsRecord); + } + + saslAuthenticationRecord = std::unique_ptr( + new Asn1ConstructedRecord(Asn1TagClass::ContextSpecific, static_cast(LdapBindRequestLayer::AuthenticationType::Sasl), saslAuthenticationRecords)); + messageRecords.push_back(saslAuthenticationRecord.get()); + } + + LdapLayer::init(messageId, LdapOperationType::BindRequest, messageRecords, controls); + } + + uint32_t LdapBindRequestLayer::getVersion() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(versionIndex)->castAs()->getValue(); + } + + std::string LdapBindRequestLayer::getName() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(nameIndex)->castAs()->getValue(); + } + + LdapBindRequestLayer::AuthenticationType LdapBindRequestLayer::getAuthenticationType() const + { + if (getLdapOperationAsn1Record()->getSubRecords().size() <= credentialIndex) + { + return LdapBindRequestLayer::AuthenticationType::NotApplicable; + } + + auto authType = getLdapOperationAsn1Record()->getSubRecords().at(credentialIndex)->getTagType(); + switch (authType) + { + case 0: + return LdapBindRequestLayer::AuthenticationType::Simple; + case 3: + return LdapBindRequestLayer::AuthenticationType::Sasl; + default: + return LdapBindRequestLayer::AuthenticationType::NotApplicable; + } + } + + std::string LdapBindRequestLayer::getSimpleAuthentication() const + { + if (getAuthenticationType() != LdapBindRequestLayer::AuthenticationType::Simple) + { + throw std::invalid_argument("Authentication type is not simple"); + } + + auto authRecord = getLdapOperationAsn1Record()->getSubRecords().at(credentialIndex)->castAs(); + return {reinterpret_cast(authRecord->getValue()), authRecord->getValueLength()}; + } + + LdapBindRequestLayer::SaslAuthentication LdapBindRequestLayer::getSaslAuthentication() const + { + if (getAuthenticationType() != LdapBindRequestLayer::AuthenticationType::Sasl) + { + throw std::invalid_argument("Authentication type is not sasl"); + } + + auto authRecord = getLdapOperationAsn1Record()->getSubRecords().at(credentialIndex)->castAs(); + std::string mechanism; + std::vector credentials; + if (authRecord->getSubRecords().size() > saslMechanismIndex) + { + mechanism = authRecord->getSubRecords().at(saslMechanismIndex)->castAs()->getValue(); + } + if (authRecord->getSubRecords().size() > saslCredentialsIndex) + { + auto credentialsAsString = authRecord->getSubRecords().at(saslCredentialsIndex)->castAs()->getValue(); + credentials.resize(credentialsAsString.size() / 2); + hexStringToByteArray(credentialsAsString, credentials.data(), credentials.size()); + } + + return {mechanism, credentials}; + } + + std::string LdapBindRequestLayer::getExtendedInfoString() const + { + switch (getAuthenticationType()) + { + case AuthenticationType::Simple: + return "simple"; + case AuthenticationType::Sasl: + return "sasl"; + default: + return "Unknown"; + } + } + + // endregion + + // region LdapBindResponseLayer + + LdapBindResponseLayer::LdapBindResponseLayer(uint16_t messageId, LdapResultCode resultCode, + const std::string& matchedDN, const std::string& diagnosticMessage, + const std::vector& referral, const std::vector& serverSaslCredentials, + const std::vector& controls) + { + std::vector additionalRecords; + std::unique_ptr serverSaslCredentialsRecord; + if (!serverSaslCredentials.empty()) + { + serverSaslCredentialsRecord = std::unique_ptr(new Asn1GenericRecord(Asn1TagClass::ContextSpecific, false, serverSaslCredentialsTagType, serverSaslCredentials.data(), serverSaslCredentials.size())); + additionalRecords.push_back(serverSaslCredentialsRecord.get()); + } + + LdapResponseLayer::init(messageId, LdapOperationType::BindResponse, resultCode, matchedDN, diagnosticMessage, referral, additionalRecords, controls); + } + + std::vector LdapBindResponseLayer::getServerSaslCredentials() const + { + try + { + auto serverSaslCredentialsRecord = getLdapOperationAsn1Record()->getSubRecords().back()->castAs(); + return {serverSaslCredentialsRecord->getValue(), serverSaslCredentialsRecord->getValue() + serverSaslCredentialsRecord->getValueLength()}; + } + catch (const std::exception&) + { + return {}; + } + } + + // endregion + + // region LdapUnbindRequestLayer + + LdapUnbindRequestLayer::LdapUnbindRequestLayer(uint16_t messageId, const std::vector& controls) + { + LdapLayer::init(messageId, LdapOperationType::UnbindRequest, {}, controls); + } + + // endregion + + // region LdapSearchRequestLayer + + const std::unordered_map> SearchRequestScopeToString { + {LdapSearchRequestLayer::SearchRequestScope::BaseObject, "BaseObject"}, + {LdapSearchRequestLayer::SearchRequestScope::SingleLevel, "SingleLevel"}, + {LdapSearchRequestLayer::SearchRequestScope::WholeSubtree, "WholeSubtree"}, + {LdapSearchRequestLayer::SearchRequestScope::Unknown, "Unknown"} + }; + + const std::unordered_map> DerefAliasesToString { + {LdapSearchRequestLayer::DerefAliases::NeverDerefAliases, "NeverDerefAliases"}, + {LdapSearchRequestLayer::DerefAliases::DerefInSearching, "DerefInSearching"}, + {LdapSearchRequestLayer::DerefAliases::DerefFindingBaseObj, "DerefFindingBaseObj"}, + {LdapSearchRequestLayer::DerefAliases::DerefAlways, "DerefAlways"}, + {LdapSearchRequestLayer::DerefAliases::Unknown, "Unknown"} + }; + + std::string LdapSearchRequestLayer::SearchRequestScope::toString() const + { + return SearchRequestScopeToString.at(m_Value); + } + + LdapSearchRequestLayer::SearchRequestScope LdapSearchRequestLayer::SearchRequestScope::fromUintValue(uint8_t value) + { + if (value >= 0 && value <= 2) + { + return static_cast(value); + } + + return LdapSearchRequestLayer::SearchRequestScope::Unknown; + } + + std::string LdapSearchRequestLayer::DerefAliases::toString() const + { + return DerefAliasesToString.at(m_Value); + } + + LdapSearchRequestLayer::DerefAliases LdapSearchRequestLayer::DerefAliases::fromUintValue(uint8_t value) + { + if (value >= 0 && value <= 3) + { + return static_cast(value); + } + + return LdapSearchRequestLayer::DerefAliases::Unknown; + } + + LdapSearchRequestLayer::LdapSearchRequestLayer( + uint16_t messageId, const std::string& baseObject, SearchRequestScope scope, DerefAliases derefAliases, + uint8_t sizeLimit, uint8_t timeLimit, bool typesOnly, Asn1Record* filterRecord, + const std::vector& attributes, const std::vector& controls) + { + Asn1OctetStringRecord baseObjectRecord(baseObject); + Asn1EnumeratedRecord scopeRecord(scope); + Asn1EnumeratedRecord derefAliasesRecord(derefAliases); + Asn1IntegerRecord sizeLimitRecord(sizeLimit); + Asn1IntegerRecord timeLimitRecord(timeLimit); + Asn1BooleanRecord typeOnlyRecord(typesOnly); + + PointerVector attributeSubRecords; + for (const auto& attribute : attributes) + { + attributeSubRecords.pushBack(new Asn1OctetStringRecord(attribute)); + } + Asn1SequenceRecord attributesRecord(attributeSubRecords); + + LdapLayer::init(messageId, LdapOperationType::SearchRequest, {&baseObjectRecord, &scopeRecord, &derefAliasesRecord, &sizeLimitRecord, &timeLimitRecord, &typeOnlyRecord, filterRecord, &attributesRecord}, controls); + } + + std::string LdapSearchRequestLayer::getBaseObject() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(baseObjectIndex)->castAs()->getValue(); + } + + LdapSearchRequestLayer::SearchRequestScope LdapSearchRequestLayer::getScope() const + { + return LdapSearchRequestLayer::SearchRequestScope::fromUintValue(getLdapOperationAsn1Record()->getSubRecords().at(scopeIndex)->castAs()->getValue()); + } + + LdapSearchRequestLayer::DerefAliases LdapSearchRequestLayer::getDerefAlias() const + { + return LdapSearchRequestLayer::DerefAliases::fromUintValue(getLdapOperationAsn1Record()->getSubRecords().at(derefAliasIndex)->castAs()->getValue()); + } + + uint8_t LdapSearchRequestLayer::getSizeLimit() const + { + return static_cast(getLdapOperationAsn1Record()->getSubRecords().at(sizeLimitIndex)->castAs()->getValue()); + } + + uint8_t LdapSearchRequestLayer::getTimeLimit() const + { + return static_cast(getLdapOperationAsn1Record()->getSubRecords().at(timeLimitIndex)->castAs()->getValue()); + } + + bool LdapSearchRequestLayer::getTypesOnly() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(typesOnlyIndex)->castAs()->getValue(); + } + + Asn1Record* LdapSearchRequestLayer::getFilter() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(filterIndex); + } + + std::vector LdapSearchRequestLayer::getAttributes() const + { + std::vector result; + if (getLdapOperationAsn1Record()->getSubRecords().size() <= attributesIndex) + { + return result; + } + + auto attributesRecord = getLdapOperationAsn1Record()->getSubRecords().at(attributesIndex)->castAs(); + for (auto attribute : attributesRecord->getSubRecords()) + { + result.push_back(attribute->castAs()->getValue()); + } + + return result; + } + + std::string LdapSearchRequestLayer::getExtendedInfoString() const + { + auto baseObject = getBaseObject(); + if (baseObject.empty()) + { + baseObject = "ROOT"; + } + + return "\"" + baseObject + "\", " + getScope().toString(); + } + + // endregion + + // region LdapSearchResultEntryLayer + + LdapSearchResultEntryLayer::LdapSearchResultEntryLayer(uint16_t messageId, const std::string& objectName, + const std::vector& attributes, const std::vector& controls) + { + PointerVector attributesSubRecords; + for (const auto& attribute : attributes) + { + PointerVector valuesSubRecords; + for (const auto& value : attribute.values) + { + valuesSubRecords.pushBack(new Asn1OctetStringRecord(value)); + } + + Asn1OctetStringRecord typeRecord(attribute.type); + Asn1SetRecord valuesRecord(valuesSubRecords); + + attributesSubRecords.pushBack(new Asn1SequenceRecord({&typeRecord, &valuesRecord})); + } + + Asn1OctetStringRecord objectNameRecord(objectName); + Asn1SequenceRecord attributesRecord(attributesSubRecords); + + LdapLayer::init(messageId, LdapOperationType::SearchResultEntry, {&objectNameRecord, &attributesRecord}, controls); + } + + std::string LdapSearchResultEntryLayer::getObjectName() const + { + return getLdapOperationAsn1Record()->getSubRecords().at(objectNameIndex)->castAs()->getValue(); + } + + std::vector LdapSearchResultEntryLayer::getAttributes() const + { + std::vector result; + + auto attributes = getLdapOperationAsn1Record()->getSubRecords().at(attributesIndex)->castAs(); + for (auto attributeRecord : attributes->getSubRecords()) + { + auto attrAsSequence = attributeRecord->castAs(); + + auto type = attrAsSequence->getSubRecords().at(attributeTypeIndex)->castAs()->getValue(); + + std::vector values; + auto valuesRecord = attrAsSequence->getSubRecords().at(attributeValueIndex)->castAs(); + + for (auto valueRecord : valuesRecord->getSubRecords()) + { + values.push_back(valueRecord->castAs()->getValue()); + } + + LdapAttribute ldapAttribute = {type, values}; + result.push_back(ldapAttribute); + } + + return result; + } + + // endregion +} diff --git a/Packet++/src/RawPacket.cpp b/Packet++/src/RawPacket.cpp index 8ad52d520f..07dbbbe21c 100644 --- a/Packet++/src/RawPacket.cpp +++ b/Packet++/src/RawPacket.cpp @@ -20,7 +20,7 @@ void RawPacket::init(bool deleteRawDataAtDestructor) RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) { - timespec nsec_time; + timespec nsec_time = {}; TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); init(deleteRawDataAtDestructor); setRawData(pRawData, rawDataLen, nsec_time, layerType); diff --git a/Packet++/src/SSLHandshake.cpp b/Packet++/src/SSLHandshake.cpp index c32a1abccd..6da7cc21ab 100644 --- a/Packet++/src/SSLHandshake.cpp +++ b/Packet++/src/SSLHandshake.cpp @@ -1117,7 +1117,13 @@ uint8_t* SSLExtension::getData() const std::string SSLServerNameIndicationExtension::getHostName() const { - uint8_t* hostNameLengthPos = getData() + sizeof(uint16_t) + sizeof(uint8_t); + auto *extensionDataPtr = getData(); + if (extensionDataPtr == nullptr) + { + return ""; + } + + uint8_t *hostNameLengthPos = extensionDataPtr + sizeof(uint16_t) + sizeof(uint8_t); uint16_t hostNameLength = be16toh(*(uint16_t*)hostNameLengthPos); char* hostNameAsCharArr = new char[hostNameLength+1]; diff --git a/Packet++/src/SomeIpSdLayer.cpp b/Packet++/src/SomeIpSdLayer.cpp index f75abe199b..89a20d92b1 100644 --- a/Packet++/src/SomeIpSdLayer.cpp +++ b/Packet++/src/SomeIpSdLayer.cpp @@ -1,6 +1,7 @@ #define LOG_MODULE PacketLogModuleSomeIpSdLayer #include "SomeIpSdLayer.h" +#include "EndianPortable.h" #include #include #include diff --git a/Packet++/src/TcpLayer.cpp b/Packet++/src/TcpLayer.cpp index abd463463c..e992cf213a 100644 --- a/Packet++/src/TcpLayer.cpp +++ b/Packet++/src/TcpLayer.cpp @@ -16,8 +16,10 @@ #include "FtpLayer.h" #include "SomeIpLayer.h" #include "SmtpLayer.h" +#include "LdapLayer.h" #include "PacketUtils.h" #include "Logger.h" +#include "DeprecationUtils.h" #include #include @@ -44,12 +46,26 @@ TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType) } } +TcpOptionBuilder::TcpOptionBuilder(const NopEolOptionEnumType optionType) +{ + switch (optionType) + { + case NopEolOptionEnumType::Eol: + init(static_cast(TcpOptionEnumType::Eol), nullptr, 0); + break; + case NopEolOptionEnumType::Nop: + default: + init(static_cast(TcpOptionEnumType::Nop), nullptr, 0); + break; + } +} + TcpOption TcpOptionBuilder::build() const { uint8_t recType = static_cast(m_RecType); size_t optionSize = m_RecValueLen + 2*sizeof(uint8_t); - if (recType == static_cast(PCPP_TCPOPT_EOL) || recType == static_cast(PCPP_TCPOPT_NOP)) + if (recType == static_cast(TcpOptionEnumType::Eol) || recType == static_cast(TcpOptionEnumType::Nop)) { if (m_RecValueLen != 0) { @@ -89,7 +105,7 @@ uint16_t TcpLayer::getDstPort() const return be16toh(getTcpHeader()->portDst); } -TcpOption TcpLayer::getTcpOption(TcpOptionType option) const +TcpOption TcpLayer::getTcpOption(const TcpOptionEnumType option) const { return m_OptionReader.getTLVRecord(static_cast(option), getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); } @@ -118,17 +134,17 @@ TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder) return addTcpOptionAt(optionBuilder, getHeaderLen()-m_NumOfTrailingBytes); } -TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType) +TcpOption TcpLayer::insertTcpOptionAfter(const TcpOptionBuilder& optionBuilder, const TcpOptionEnumType prevOptionType) { int offset = 0; - if (prevOptionType == TCPOPT_Unknown) + if (prevOptionType == TcpOptionEnumType::Unknown) { offset = sizeof(tcphdr); } else { - TcpOption prevOpt = getTcpOption(prevOptionType); + const TcpOption prevOpt = getTcpOption(prevOptionType); if (prevOpt.isNull()) { PCPP_LOG_ERROR("Previous option of type " << static_cast(prevOptionType) << " not found, cannot add a new TCP option"); @@ -141,9 +157,9 @@ TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, Tcp return addTcpOptionAt(optionBuilder, offset); } -bool TcpLayer::removeTcpOption(TcpOptionType optionType) +bool TcpLayer::removeTcpOption(const TcpOptionEnumType optionType) { - TcpOption opt = getTcpOption(optionType); + const TcpOption opt = getTcpOption(optionType); if (opt.isNull()) { return false; @@ -176,7 +192,7 @@ bool TcpLayer::removeTcpOption(TcpOptionType optionType) bool TcpLayer::removeAllTcpOptions() { - int offset = sizeof(tcphdr); + const int offset = sizeof(tcphdr); if (!shortenLayer(offset, getHeaderLen()-offset)) return false; @@ -187,7 +203,7 @@ bool TcpLayer::removeAllTcpOptions() return true; } -TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset) +TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, const int offset) { TcpOption newOption = optionBuilder.build(); if (newOption.isNull()) @@ -225,7 +241,7 @@ TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int of return TcpOption(newOptPtr); } -void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) +void TcpLayer::adjustTcpOptionTrailer(const size_t totalOptSize) { int newNumberOfTrailingBytes = 0; while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) @@ -244,7 +260,7 @@ void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) getTcpHeader()->dataOffset = (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes)/4; } -uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket) +uint16_t TcpLayer::calculateChecksum(const bool writeResultToPacket) { tcphdr* tcpHdr = getTcpHeader(); uint16_t checksumRes = 0; @@ -295,7 +311,7 @@ void TcpLayer::initLayer() getTcpHeader()->dataOffset = sizeof(tcphdr)/4; } -TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) +TcpLayer::TcpLayer(uint8_t* data, const size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = TCP; m_NumOfTrailingBytes = 0; @@ -306,7 +322,7 @@ TcpLayer::TcpLayer() initLayer(); } -TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst) +TcpLayer::TcpLayer(const uint16_t portSrc, const uint16_t portDst) { initLayer(); getTcpHeader()->portDst = htobe16(portDst); @@ -389,6 +405,12 @@ void TcpLayer::parseNextLayer() m_NextLayer = new SmtpResponseLayer(payload, payloadLen, this, m_Packet); else if (SmtpLayer::isSmtpPort(portDst) && SmtpLayer::isDataValid(payload, payloadLen)) m_NextLayer = new SmtpRequestLayer(payload, payloadLen, this, m_Packet); + else if (LdapLayer::isLdapPort(portDst) || LdapLayer::isLdapPort(portSrc)) + { + m_NextLayer = LdapLayer::parseLdapMessage(payload, payloadLen, this, m_Packet); + if (!m_NextLayer) + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } else m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } @@ -431,4 +453,72 @@ std::string TcpLayer::toString() const return result; } +/// ~~~~~~~~ +/// TcpLayer Deprecated Code +/// ~~~~~~~~ + +DISABLE_WARNING_PUSH +DISABLE_WARNING_DEPRECATED +TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType) +{ + int offset = 0; + + if (prevOptionType == TcpOptionType::TCPOPT_Unknown) + { + offset = sizeof(tcphdr); + } + else + { + TcpOption prevOpt = getTcpOption(prevOptionType); + if (prevOpt.isNull()) + { + PCPP_LOG_ERROR("Previous option of type " << static_cast(prevOptionType) << " not found, cannot add a new TCP option"); + return TcpOption(nullptr); + } + + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } + + return addTcpOptionAt(optionBuilder, offset); +} + +TcpOption TcpLayer::getTcpOption(TcpOptionType option) const +{ + return m_OptionReader.getTLVRecord(static_cast(option), getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +} + +bool TcpLayer::removeTcpOption(TcpOptionType optionType) +{ + TcpOption opt = getTcpOption(optionType); + if (opt.isNull()) + { + return false; + } + + // calculate total TCP option size + TcpOption curOpt = getFirstTcpOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) + { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextTcpOption(curOpt); + } + totalOptSize -= opt.getTotalSize(); + + + int offset = opt.getRecordBasePtr() - m_Data; + + if (!shortenLayer(offset, opt.getTotalSize())) + { + return false; + } + + adjustTcpOptionTrailer(totalOptSize); + + m_OptionReader.changeTLVRecordCount(-1); + + return true; +} +DISABLE_WARNING_POP + } // namespace pcpp diff --git a/Packet++/src/TelnetLayer.cpp b/Packet++/src/TelnetLayer.cpp index 3f0e58b272..f1fe581786 100644 --- a/Packet++/src/TelnetLayer.cpp +++ b/Packet++/src/TelnetLayer.cpp @@ -40,7 +40,7 @@ size_t TelnetLayer::distanceToNextIAC(uint8_t *startPos, size_t maxLength) addition += maxLength - currentOffset; currentOffset = currentOffset + addition; // "FF FF" means data continue - } while (pos && (pos[1] == static_cast(TelnetCommand::InterpretAsCommand)) && (currentOffset < maxLength)); + } while (pos && ((pos + 1) < (startPos + maxLength)) && (pos[1] == static_cast(TelnetCommand::InterpretAsCommand)) && (currentOffset < maxLength)); return addition; } diff --git a/Pcap++/CMakeLists.txt b/Pcap++/CMakeLists.txt index a23efa7e17..f28fcb6be6 100644 --- a/Pcap++/CMakeLists.txt +++ b/Pcap++/CMakeLists.txt @@ -1,11 +1,13 @@ add_library( Pcap++ + src/DeviceUtils.cpp $<$:src/DpdkDevice.cpp> $<$:src/DpdkDeviceList.cpp> $<$:src/KniDevice.cpp> $<$:src/KniDeviceList.cpp> $<$:src/LinuxNicInformationSocket.cpp> $<$:src/MBufRawPacket.cpp> + src/PcapUtils.cpp src/NetworkUtils.cpp src/PcapFileDevice.cpp src/PcapDevice.cpp diff --git a/Pcap++/header/DeviceUtils.h b/Pcap++/header/DeviceUtils.h new file mode 100644 index 0000000000..7a33e2e277 --- /dev/null +++ b/Pcap++/header/DeviceUtils.h @@ -0,0 +1,24 @@ +#pragma once + +/// @file + +#include +#include "IpAddress.h" +#include "PcapUtils.h" + +namespace pcpp +{ + /// @cond PCPP_INTERNAL + + namespace internal + { + /** + * Fetches a list of all network devices on the local machine that LibPcap/WinPcap/NPcap can find. + * @return A smart pointer to an interface list structure. + * @throws std::runtime_error The system encountered an error fetching the devices. + */ + std::unique_ptr getAllLocalPcapDevices(); + } + + /// @endcond +} diff --git a/Pcap++/header/DpdkDevice.h b/Pcap++/header/DpdkDevice.h index e757ed03ad..069bcc6df4 100644 --- a/Pcap++/header/DpdkDevice.h +++ b/Pcap++/header/DpdkDevice.h @@ -754,12 +754,12 @@ namespace pcpp * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port * configuration and startup failed */ - bool open() { return openMultiQueues(1, 1); }; + bool open() override { return openMultiQueues(1, 1); }; /** * Close the DpdkDevice. When device is closed it's not possible work with it */ - void close(); + void close() override; private: @@ -803,6 +803,7 @@ namespace pcpp int m_Id; MacAddress m_MacAddress; + int16_t m_DeviceSocketId; uint16_t m_DeviceMtu; uint16_t m_MBufDataSize; struct rte_mempool* m_MBufMempool; diff --git a/Pcap++/header/DpdkDeviceList.h b/Pcap++/header/DpdkDeviceList.h index fa25a2afc0..e1336e982d 100644 --- a/Pcap++/header/DpdkDeviceList.h +++ b/Pcap++/header/DpdkDeviceList.h @@ -108,10 +108,10 @@ namespace pcpp /** * A static method that has to be called once at the startup of every application that uses DPDK. It does several things: - * - verifies huge-pages are set and DPDK kernel module is loaded (these are set by the setup_dpdk.py external script that - * has to be run before application is started) - * - initializes the DPDK infrastructure - * - creates DpdkDevice instances for all ports available for DPDK + * - verifies huge-pages are set and DPDK kernel module is loaded, unless specifically asked not to (these are set by + * the setup_dpdk.py external script that has to be run before application is started) + * - initializes the DPDK infrastructure + * - creates DpdkDevice instances for all ports available for DPDK * * @param[in] coreMask The cores to initialize DPDK with. After initialization, DPDK will only be able to use these cores * for its work. The core mask should have a bit set for every core to use. For example: if the user want to use cores 1,2 @@ -124,13 +124,14 @@ namespace pcpp * @param[in] masterCore The core DPDK will use as master to control all worker thread. The default, unless set otherwise, is 0 * @param[in] initDpdkArgc Number of optional arguments * @param[in] initDpdkArgv Optional arguments - * @param[in] appName program name to be provided for the DPDK + * @param[in] appName Program name to be provided for the DPDK + * @param[in] verifyHugePagesAndDriver Verify huge-pages are set and DPDK kernel module is loaded. The default value it true * @return True if initialization succeeded or false if huge-pages or DPDK kernel driver are not loaded, if mBufPoolSizePerDevice * isn't power of 2 minus 1, if DPDK infra initialization failed or if DpdkDevice initialization failed. Anyway, if this method * returned false it's impossible to use DPDK with PcapPlusPlus. You can get some more details about mbufs and pools in * DpdkDevice.h file description or in DPDK web site */ - static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint16_t mBufDataSize = 0, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = NULL, const std::string& appName = "pcapplusplusapp"); + static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint16_t mBufDataSize = 0, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = nullptr, const std::string& appName = "pcapplusplusapp", bool verifyHugePagesAndDriver = true); /** * Get a DpdkDevice by port ID diff --git a/Pcap++/header/MBufRawPacket.h b/Pcap++/header/MBufRawPacket.h index 4d4cefa483..699d33f8b6 100644 --- a/Pcap++/header/MBufRawPacket.h +++ b/Pcap++/header/MBufRawPacket.h @@ -143,7 +143,7 @@ namespace pcpp /** * @return MBufRawPacket object type */ - virtual inline uint8_t getObjectType() const { return MBUFRAWPACKET_OBJECT_TYPE; } + inline uint8_t getObjectType() const override { return MBUFRAWPACKET_OBJECT_TYPE; } /** * An assignment operator for this class. Copies the data from the mbuf attached to the other MBufRawPacket to the mbuf diff --git a/Pcap++/header/PcapFileDevice.h b/Pcap++/header/PcapFileDevice.h index 5a512fd243..81da66054f 100644 --- a/Pcap++/header/PcapFileDevice.h +++ b/Pcap++/header/PcapFileDevice.h @@ -16,6 +16,20 @@ typedef struct pcap_dumper pcap_dumper_t; */ namespace pcpp { + /** + * @enum FileTimestampPrecision + * An enumeration representing the precision of timestamps in a pcap file. + * The precision can be Unknown, Micro, or Nano. + */ + enum class FileTimestampPrecision : int8_t + { + /// Precision is unknown or not set/determined + Unknown = -1, + /// Precision is in microseconds. + Microseconds = 0, + /// Precision is in nanoseconds. + Nanoseconds = 1 + }; /** * @class IFileDevice @@ -36,13 +50,12 @@ namespace pcpp */ std::string getFileName() const; - //override methods /** * Close the file */ - virtual void close(); + void close() override; }; @@ -103,6 +116,7 @@ namespace pcpp class PcapFileReaderDevice : public IFileReaderDevice { private: + FileTimestampPrecision m_Precision; LinkLayerType m_PcapLinkLayerType; // private copy c'tor @@ -115,7 +129,7 @@ namespace pcpp * isn't opened yet, so reading packets will fail. For opening the file call open() * @param[in] fileName The full path of the file to read */ - PcapFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} + PcapFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_Precision(FileTimestampPrecision::Unknown), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} /** * A destructor for this class @@ -127,6 +141,17 @@ namespace pcpp */ LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } + /** + * @return The precision of the timestamps in the file. If the platform supports nanosecond precision, this method will return + * nanoseconds even if the file has microseconds since libpcap scales timestamps before supply. Otherwise, it will return microseconds. + */ + FileTimestampPrecision getTimestampPrecision() const { return m_Precision; } + + /** + * A static method that checks if nano-second precision is supported in the current platform and environment + * @return True if nano-second precision is supported, false otherwise + */ + static bool isNanoSecondPrecisionSupported(); //overridden methods @@ -384,6 +409,7 @@ namespace pcpp pcap_dumper_t* m_PcapDumpHandler; LinkLayerType m_PcapLinkLayerType; bool m_AppendMode; + FileTimestampPrecision m_Precision; FILE* m_File; // private copy c'tor @@ -398,8 +424,9 @@ namespace pcpp * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() * @param[in] fileName The full path of the file * @param[in] linkLayerType The link layer type all packet in this file will be based on. The default is Ethernet + * @param[in] nanosecondsPrecision A boolean indicating whether to write timestamps in nano-precision. If set to false, timestamps will be written in micro-precision */ - PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType = LINKTYPE_ETHERNET); + PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType = LINKTYPE_ETHERNET, bool nanosecondsPrecision = false); /** * A destructor for this class @@ -414,7 +441,7 @@ namespace pcpp * or if the packet link layer type is different than the one defined for the file * (in all cases, an error will be printed to log) */ - bool writePacket(RawPacket const& packet); + bool writePacket(RawPacket const& packet) override; /** * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change @@ -423,7 +450,18 @@ namespace pcpp * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error * log will be printed) or if at least one of the packets wasn't written successfully to the file */ - bool writePackets(const RawPacketVector& packets); + bool writePackets(const RawPacketVector& packets) override; + + /** + * @return The precision of the timestamps in the file. + */ + FileTimestampPrecision getTimestampPrecision() const { return m_Precision; } + + /** + * A static method that checks if nano-second precision is supported in the current platform and environment + * @return True if nano-second precision is supported, false otherwise + */ + static bool isNanoSecondPrecisionSupported(); //override methods @@ -433,7 +471,7 @@ namespace pcpp * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason * (an error will be printed to log) */ - virtual bool open(); + bool open() override; /** * Same as open(), but enables to open the file in append mode in which packets will be appended to the file @@ -445,12 +483,12 @@ namespace pcpp * different from current file link type. In case appendMode is set to false, please refer to open() for return * values */ - bool open(bool appendMode); + bool open(bool appendMode) override; /** * Flush and close the pacp file */ - virtual void close(); + void close() override; /** * Flush packets to disk. @@ -461,7 +499,7 @@ namespace pcpp * Get statistics of packets written so far. * @param[out] stats The stats struct where stats are returned */ - virtual void getStatistics(PcapStats& stats) const; + void getStatistics(PcapStats& stats) const override; }; diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 256f4a582d..c632ae68c2 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -20,7 +20,7 @@ struct bpf_program; * may output syntax errors that are hard to understand. My experience with BPF was not good, so I decided to make the filters mechanism more * structured, easier to understand and less error-prone by creating classes that represent filters. Each possible filter phrase is represented * by a class. The filter, at the end, is that class.
- * For example: the filter "src host 1.1.1.1" will be represented by IPFilter instance; "dst port 80" will be represented by PortFilter, and + * For example: the filter "src net 1.1.1.1" will be represented by IPFilter instance; "dst port 80" will be represented by PortFilter, and * so on.
* So what about complex filters that involve "and", "or"? There are also 2 classes: AndFilter and OrFilter that can store more filters (in a * composite idea) and connect them by "and" or "or". For example: "src host 1.1.1.1 and dst port 80" will be represented by an AndFilter that @@ -69,6 +69,18 @@ namespace pcpp LESS_OR_EQUAL } FilterOperator; + namespace internal + { + /** + * @class BpfProgramDeleter + * A deleter that cleans up a bpf_program object. + */ + struct BpfProgramDeleter + { + void operator()(bpf_program* ptr) const; + }; + } + /** * @class BpfFilterWrapper * A wrapper class for BPF filtering. Enables setting a BPF filter and matching it against a packet @@ -78,7 +90,7 @@ namespace pcpp private: std::string m_FilterStr; LinkLayerType m_LinkType; - bpf_program* m_Program; + std::unique_ptr m_Program; void freeProgram(); @@ -90,9 +102,17 @@ namespace pcpp BpfFilterWrapper(); /** - * A d'tor for this class. Makes sure to clear the bpf_program object if was previously set. + * A copy constructor for this class. + * @param[in] other The instance to copy from + */ + BpfFilterWrapper(const BpfFilterWrapper& other); + + /** + * A copy assignment operator for this class. + * @param[in] other An instance of IPNetwork to assign + * @return A reference to the assignee */ - ~BpfFilterWrapper(); + BpfFilterWrapper& operator=(const BpfFilterWrapper& other); /** * Set a filter. This method receives a filter in BPF syntax (https://biot.com/capstats/bpf.html) and an optional link type, @@ -243,62 +263,141 @@ namespace pcpp class IPFilter : public IFilterWithDirection { private: - std::string m_Address; - std::string m_IPv4Mask; - int m_Len; - void convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const; - void convertToIPAddressWithLen(std::string& ipAddrmodified) const; + IPAddress m_Address; + IPNetwork m_Network; public: /** - * The basic constructor that creates the filter from an IPv4 address and direction (source or destination) - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail + * The basic constructor that creates the filter from an IP address string and direction (source or destination) + * @param[in] ipAddress The IP address to build the filter with. * @param[in] dir The address direction to filter (source or destination) + * @throws std::invalid_argument The provided address is not a valid IPv4 or IPv6 address. */ - IPFilter(const std::string& ipAddress, Direction dir) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(0) {} + IPFilter(const std::string& ipAddress, Direction dir) : IPFilter(IPAddress(ipAddress), dir) {} + + /** + * The basic constructor that creates the filter from an IP address and direction (source or destination) + * @param[in] ipAddress The IP address to build the filter with. + * @param[in] dir The address direction to filter (source or destination) + */ + IPFilter(const IPAddress& ipAddress, Direction dir) : IFilterWithDirection(dir), m_Address(ipAddress), m_Network(ipAddress) {} /** * A constructor that enable to filter only part of the address by using a mask (aka subnet). For example: "filter only IP addresses that matches * the subnet 10.0.0.x" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail + * @param[in] ipAddress The IP address to use. Only the part of the address that is not masked will be matched. For example: if the address + * is "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that will be matched is "1.2.3.X". + * @param[in] dir The address direction to filter (source or destination) + * @param[in] netmask The mask to use. The mask should be a valid IP address in either IPv4 dotted-decimal format (e.g., 255.255.255.0) or IPv6 colon-separated hexadecimal format (e.g., FFFF:FFFF:FFFF:FFFF::). + * @throws std::invalid_argument The provided address is not a valid IP address or the provided netmask string is invalid.. + */ + IPFilter(const std::string& ipAddress, Direction dir, const std::string& netmask) : IPFilter(IPv4Address(ipAddress), dir, netmask) {} + + /** + * A constructor that enable to filter only part of the address by using a mask (aka subnet). For example: + * "filter only IP addresses that matches the subnet 10.0.0.x" + * @param[in] ipAddress The IP address to use. Only the part of the address that is not masked will be + * matched. For example: if the address is "1.2.3.4" and the mask is "255.255.255.0" than the part of the + * address that will be matched is "1.2.3.X". * @param[in] dir The address direction to filter (source or destination) - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail + * @param[in] netmask The mask to use. The mask should be a valid IP address in either IPv4 dotted-decimal format (e.g., 255.255.255.0) or IPv6 colon-separated hexadecimal format (e.g., FFFF:FFFF:FFFF:FFFF::). + * @throws std::invalid_argument The provided netmask string is invalid. */ - IPFilter(const std::string& ipAddress, Direction dir, const std::string& ipv4Mask) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(ipv4Mask), m_Len(0) {} + IPFilter(const IPAddress& ipAddress, Direction dir, const std::string& netmask) : IFilterWithDirection(dir), m_Address(ipAddress), m_Network(ipAddress, netmask) {} /** * A constructor that enables to filter by a subnet. For example: "filter only IP addresses that matches the subnet 10.0.0.3/24" which means * the part of the address that will be matched is "10.0.0.X" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the subnet is "/24" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail + * @param[in] ipAddress The IP address to use. Only the part of the address that is not masked will be matched. For example: if the address + * is "1.2.3.4" and the subnet is "/24" than the part of the address that will be matched is "1.2.3.X". * @param[in] dir The address direction to filter (source or destination) - * @param[in] len The subnet to use (e.g "/24") + * @param[in] len The subnet to use (e.g "/24"). Acceptable subnet values are [0, 32] for IPv4 and [0, 128] for IPv6. + * @throws std::invalid_argument The provided address is not a valid IPv4 or IPv6 address or the provided length is out of acceptable range. + */ + IPFilter(const std::string& ipAddress, Direction dir, int len) : IPFilter(IPAddress(ipAddress), dir, len) {} + + /** + * A constructor that enables to filter by a subnet. For example: "filter only IP addresses that matches the + * subnet 10.0.0.3/24" which means the part of the address that will be matched is "10.0.0.X" + * @param[in] ipAddress The IP address to use. Only the part of the address that is not masked will be matched. + * For example: if the address is "1.2.3.4" and the subnet is "/24" than the part of the address that will be + * matched is "1.2.3.X". + * @param[in] dir The address direction to filter (source or destination) + * @param[in] len The subnet to use (e.g "/24"). Acceptable subnet values are [0, 32] for IPv4 and [0, 128] for IPv6. + * @throws std::invalid_argument The provided length is out of acceptable range. */ - IPFilter(const std::string& ipAddress, Direction dir, int len) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(len) {} + IPFilter(const IPAddress& ipAddress, Direction dir, int len) : IFilterWithDirection(dir), m_Address(ipAddress), m_Network(ipAddress, len) {} + + /** + * A constructor that enables to filter by a predefined network object. + * @param[in] network The network to use when filtering. IP address and subnet mask are taken from the network object. + * @param[in] dir The address direction to filter (source or destination) + */ + IPFilter(const IPNetwork& network, Direction dir) : IFilterWithDirection(dir), m_Address(network.getNetworkPrefix()), m_Network(network) {} void parseToString(std::string& result) override; /** - * Set the IPv4 address - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail + * Set the network to build the filter with. + * @param[in] network The IP Network object to be used when building the filter. */ - void setAddr(const std::string& ipAddress) { m_Address = ipAddress; } + void setNetwork(const IPNetwork& network) + { + m_Network = network; + m_Address = m_Network.getNetworkPrefix(); + } /** - * Set the IPv4 mask - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail + * Set the IP address + * @param[in] ipAddress The IP address to build the filter with. + * @throws std::invalid_argument The provided string does not represent a valid IP address. */ - void setMask(const std::string& ipv4Mask) { m_IPv4Mask = ipv4Mask; m_Len = 0; } + void setAddr(const std::string& ipAddress) { this->setAddr(IPAddress(ipAddress)); } /** - * Set the subnet + * Set the IP address + * @param[in] ipAddress The IP address to build the filter with. + * @remarks Alternating between IPv4 and IPv6 can have unintended consequences on the subnet mask. + * Setting an IPv4 address when the prefix length is over 32 make the new prefix length 32. + * Setting an IPv6 address will keep the current IPv4 prefix mask length. + */ + void setAddr(const IPAddress& ipAddress) + { + m_Address = ipAddress; + uint8_t newPrefixLen = m_Network.getPrefixLen(); + if (m_Address.isIPv4() && newPrefixLen > 32u) + { + newPrefixLen = 32u; + } + + m_Network = IPNetwork(m_Address, newPrefixLen); + } + + /** + * Set the subnet mask + * @param[in] netmask The mask to use. The mask should match the IP versrion and be in a valid format. + * Valid formats: + * IPv4 - (X.X.X.X) - 'X' - a number in the range of 0 and 255 (inclusive)): + * IPv6 - (YYYY:YYYY:YYYY:YYYY:YYYY:YYYY:YYYY:YYYY) - 'Y' - a hexadecimal digit [0 - 9, A - F]. Short form IPv6 formats are allowed. + * @throws std::invalid_argument The provided netmask is invalid or does not correspond to the current IP address version. + */ + void setMask(const std::string& netmask) { m_Network = IPNetwork(m_Address, netmask); } + + /** + * Clears the subnet mask. + */ + void clearMask() { this->clearLen(); } + + /** + * Set the subnet (IPv4) or prefix length (IPv6). Acceptable subnet values are [0, 32] for IPv4 and [0, 128] for IPv6. * @param[in] len The subnet to use (e.g "/24") + * @throws std::invalid_argument The provided length is out of acceptable range. + */ + void setLen(const int len) { m_Network = IPNetwork(m_Address, len); } + + /** + * Clears the subnet mask length. */ - void setLen(int len) { m_IPv4Mask = ""; m_Len = len; } + void clearLen() { m_Network = IPNetwork(m_Address); } }; @@ -539,7 +638,7 @@ namespace pcpp OR, }; - namespace detail + namespace internal { /* Could potentially be moved into CompositeLogicFilter as a private member function, with if constexpr when C++17 is the minimum supported standard.*/ /** @@ -572,7 +671,7 @@ namespace pcpp result += '(' + innerFilter + ')'; if (m_FilterList.cend() - 1 != it) { - result += detail::getCompositeLogicOpDelimiter(); + result += internal::getCompositeLogicOpDelimiter(); } } } diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index 9f086c23bf..e4b88ba2f0 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -257,7 +257,7 @@ namespace pcpp /** * A destructor for this class */ - virtual ~PcapLiveDevice(); + ~PcapLiveDevice() override; /** * @return The type of the device (libPcap, WinPcap/Npcap or a remote device) @@ -438,7 +438,7 @@ namespace pcpp * @param[in] packetPayloadLength The length of the IP layer of the packet * @return True if the packetPayloadLength is less than or equal to the device MTU */ - bool doMtuCheck(int packetPayloadLength); + bool doMtuCheck(int packetPayloadLength) const; /** * Send a RawPacket to the network @@ -554,7 +554,7 @@ namespace pcpp * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log * as well */ - bool open(); + bool open() override; /** * Enables to open a device in a non-default configuration. Configuration has parameters like packet buffer timeout & size, open in @@ -564,18 +564,20 @@ namespace pcpp */ bool open(const DeviceConfiguration& config); - void close(); + void close() override; /** * Clones the current device class * @return Pointer to the copied class */ - PcapLiveDevice* clone(); + PcapLiveDevice* clone() const; - virtual void getStatistics(IPcapDevice::PcapStats& stats) const; + void getStatistics(IPcapDevice::PcapStats& stats) const override; protected: pcap_t* doOpen(const DeviceConfiguration& config); + + virtual PcapLiveDevice* cloneInternal(pcap_if_t& devInterface) const; }; } // namespace pcpp diff --git a/Pcap++/header/PcapLiveDeviceList.h b/Pcap++/header/PcapLiveDeviceList.h index 22d3b02c55..7a93fe9d74 100644 --- a/Pcap++/header/PcapLiveDeviceList.h +++ b/Pcap++/header/PcapLiveDeviceList.h @@ -3,6 +3,7 @@ #include "IpAddress.h" #include "PcapLiveDevice.h" #include +#include /// @file @@ -23,20 +24,26 @@ namespace pcpp class PcapLiveDeviceList { private: - std::vector m_LiveDeviceList; + std::vector> m_LiveDeviceList; + // Vector of raw device pointers to keep the signature of getPcapLiveDevicesList, as it returns a reference. + std::vector m_LiveDeviceListView; std::vector m_DnsServers; // private c'tor PcapLiveDeviceList(); - // private copy c'tor - PcapLiveDeviceList( const PcapLiveDeviceList& other ); - PcapLiveDeviceList& operator=(const PcapLiveDeviceList& other); void init(); void setDnsServers(); + + void updateLiveDeviceListView() const; public: + PcapLiveDeviceList(const PcapLiveDeviceList&) = delete; + PcapLiveDeviceList(PcapLiveDeviceList&&) noexcept = delete; + PcapLiveDeviceList& operator=(const PcapLiveDeviceList&) = delete; + PcapLiveDeviceList& operator=(PcapLiveDeviceList&&) noexcept = delete; + /** * The access method to the singleton * @return The singleton instance of this class @@ -50,7 +57,7 @@ namespace pcpp /** * @return A vector containing pointers to all live devices currently installed on the machine */ - const std::vector& getPcapLiveDevicesList() const { return m_LiveDeviceList; } + const std::vector& getPcapLiveDevicesList() const { return m_LiveDeviceListView; }; /** * Get a pointer to the live device by its IP address. IP address can be both IPv4 or IPv6 @@ -110,9 +117,6 @@ namespace pcpp * Reset the live device list and DNS server list, meaning clear and refetch them */ void reset(); - - // d'tor - ~PcapLiveDeviceList(); }; } // namespace pcpp diff --git a/Pcap++/header/PcapRemoteDevice.h b/Pcap++/header/PcapRemoteDevice.h index 860a327888..3ea51e130a 100644 --- a/Pcap++/header/PcapRemoteDevice.h +++ b/Pcap++/header/PcapRemoteDevice.h @@ -3,6 +3,7 @@ #if defined(_WIN32) #include +#include #include "PcapLiveDevice.h" @@ -81,15 +82,10 @@ namespace pcpp private: IPAddress m_RemoteMachineIpAddress; uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; + std::shared_ptr m_RemoteAuthentication; // c'tor is private, as only PcapRemoteDeviceList should create instances of it, and it'll create only one for every remote interface - PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort); - - // private copy c'tor - PcapRemoteDevice( const PcapRemoteDevice& other ); - // private assignment operator - PcapRemoteDevice& operator=(const PcapRemoteDevice& other); + PcapRemoteDevice(pcap_if_t* iface, std::shared_ptr remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort); static void* remoteDeviceCaptureThreadMain(void *ptr); @@ -97,7 +93,12 @@ namespace pcpp ThreadStart getCaptureThreadStart(); public: - virtual ~PcapRemoteDevice() {} + PcapRemoteDevice(const PcapRemoteDevice&) = delete; + PcapRemoteDevice(PcapRemoteDevice&&) noexcept = delete; + PcapRemoteDevice& operator=(const PcapRemoteDevice&) = delete; + PcapRemoteDevice& operator=(PcapRemoteDevice&&) noexcept = delete; + + ~PcapRemoteDevice() override {} /** * @return The IP address of the remote machine where packets are transmitted from the remote machine to the client machine @@ -109,21 +110,22 @@ namespace pcpp */ uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } - //overridden methods - - virtual LiveDeviceType getDeviceType() const { return RemoteDevice; } + /** + * @return The type of the device (libPcap, WinPcap/Npcap or a remote device) + */ + LiveDeviceType getDeviceType() const override { return RemoteDevice; } /** * MTU isn't supported for remote devices * @return 0 */ - virtual uint32_t getMtu() const; + uint32_t getMtu() const override; /** * MAC address isn't supported for remote devices * @return MacAddress#Zero */ - virtual MacAddress getMacAddress() const; + MacAddress getMacAddress() const override; /** * Open the device using pcap_open. Opening the device makes the connection to the remote daemon (including authentication if needed @@ -134,9 +136,9 @@ namespace pcpp * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log * as well, including the WinPcap/Npcap error if exists */ - virtual bool open(); + bool open() override; - virtual void getStatistics(IPcapDevice::PcapStats& stats) const; + void getStatistics(IPcapDevice::PcapStats& stats) const override; }; } // namespace pcpp diff --git a/Pcap++/header/PcapRemoteDeviceList.h b/Pcap++/header/PcapRemoteDeviceList.h index 904866c40c..4d9e09a3d3 100644 --- a/Pcap++/header/PcapRemoteDeviceList.h +++ b/Pcap++/header/PcapRemoteDeviceList.h @@ -2,6 +2,7 @@ #if defined(_WIN32) +#include #include "IpAddress.h" #include "PcapRemoteDevice.h" @@ -30,13 +31,10 @@ namespace pcpp std::vector m_RemoteDeviceList; IPAddress m_RemoteMachineIpAddress; uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; + std::shared_ptr m_RemoteAuthentication; // private c'tor. User should create the list via static methods PcapRemoteDeviceList::getRemoteDeviceList() - PcapRemoteDeviceList() : m_RemoteMachinePort(0), m_RemoteAuthentication(NULL) {} - // private copy c'tor - PcapRemoteDeviceList(const PcapRemoteDeviceList& other); - PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList& other); + PcapRemoteDeviceList() : m_RemoteMachinePort(0), m_RemoteAuthentication(nullptr) {} void setRemoteMachineIpAddress(const IPAddress& ipAddress); void setRemoteMachinePort(uint16_t port); @@ -53,6 +51,11 @@ namespace pcpp */ typedef typename std::vector::const_iterator ConstRemoteDeviceListIterator; + PcapRemoteDeviceList(const PcapRemoteDeviceList&) = delete; + PcapRemoteDeviceList(PcapRemoteDeviceList&&) noexcept = delete; + PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList&) = delete; + PcapRemoteDeviceList& operator=(PcapRemoteDeviceList&&) noexcept = delete; + ~PcapRemoteDeviceList(); /** diff --git a/Pcap++/header/PcapUtils.h b/Pcap++/header/PcapUtils.h new file mode 100644 index 0000000000..d9f76033fe --- /dev/null +++ b/Pcap++/header/PcapUtils.h @@ -0,0 +1,35 @@ +#pragma once + +// Forward declarations +struct pcap; +typedef pcap pcap_t; +struct pcap_if; +typedef pcap_if pcap_if_t; + +namespace pcpp +{ + /// @cond PCPP_INTERNAL + + namespace internal + { + /** + * @class PcapCloseDeleter + * A deleter that cleans up a pcap_t structure by calling pcap_close. + */ + struct PcapCloseDeleter + { + void operator()(pcap_t* ptr) const; + }; + + /** + * @class PcapFreeAllDevsDeleter + * A deleter that frees an interface list of pcap_if_t ptr by calling 'pcap_freealldevs' function on it. + */ + struct PcapFreeAllDevsDeleter + { + void operator()(pcap_if_t* ptr) const; + }; + } + + /// @endcond +} diff --git a/Pcap++/header/PfRingDeviceList.h b/Pcap++/header/PfRingDeviceList.h index 48e43ddaa4..d800862c21 100644 --- a/Pcap++/header/PfRingDeviceList.h +++ b/Pcap++/header/PfRingDeviceList.h @@ -2,6 +2,7 @@ // GCOVR_EXCL_START +#include #include "PfRingDevice.h" /// @file @@ -21,18 +22,17 @@ namespace pcpp class PfRingDeviceList { private: - std::vector m_PfRingDeviceList; + std::vector> m_PfRingDeviceList; + std::vector m_PfRingDeviceListView; std::string m_PfRingVersion; PfRingDeviceList(); - // private copy c'tor - PfRingDeviceList(const PfRingDeviceList& other); - PfRingDeviceList& operator=(const PfRingDeviceList& other); - // private d'tor - ~PfRingDeviceList(); - - void calcPfRingVersion(void* ring); public: + PfRingDeviceList(const PfRingDeviceList&) = delete; + PfRingDeviceList(PfRingDeviceList&&) noexcept = delete; + PfRingDeviceList& operator=(const PfRingDeviceList&) = delete; + PfRingDeviceList& operator=(PfRingDeviceList&&) noexcept = delete; + /** * A static method that returns the singleton object for PfRingDeviceList * @return PfRingDeviceList singleton @@ -47,7 +47,7 @@ namespace pcpp * Return a list of all available PF_RING devices * @return a list of all available PF_RING devices */ - const std::vector& getPfRingDevicesList() const { return m_PfRingDeviceList; } + const std::vector& getPfRingDevicesList() const { return m_PfRingDeviceListView; } /** * Get a PF_RING device by name. The name is the Linux interface name which appears in ifconfig diff --git a/Pcap++/header/RawSocketDevice.h b/Pcap++/header/RawSocketDevice.h index 359e4463d1..ceb863d91c 100644 --- a/Pcap++/header/RawSocketDevice.h +++ b/Pcap++/header/RawSocketDevice.h @@ -135,12 +135,12 @@ namespace pcpp * Open the device by creating a raw socket and binding it to the network interface specified in the c'tor * @return True if device was opened successfully, false otherwise with a corresponding error log message */ - virtual bool open(); + bool open() override; /** * Close the raw socket */ - virtual void close(); + void close() override; private: diff --git a/Pcap++/header/WinPcapLiveDevice.h b/Pcap++/header/WinPcapLiveDevice.h index 38a258d541..153f80d3d2 100644 --- a/Pcap++/header/WinPcapLiveDevice.h +++ b/Pcap++/header/WinPcapLiveDevice.h @@ -55,6 +55,9 @@ namespace pcpp * setMinAmountOfDataToCopyFromKernelToApplication()) */ int getMinAmountOfDataToCopyFromKernelToApplication() const { return m_MinAmountOfDataToCopyFromKernelToApplication; } + + protected: + WinPcapLiveDevice* cloneInternal(pcap_if_t& devInterface) const override; }; } // namespace pcpp diff --git a/Pcap++/src/DeviceUtils.cpp b/Pcap++/src/DeviceUtils.cpp new file mode 100644 index 0000000000..785c726b47 --- /dev/null +++ b/Pcap++/src/DeviceUtils.cpp @@ -0,0 +1,27 @@ +#include "DeviceUtils.h" + +#include +#include + +#include "pcap.h" +#include "Logger.h" +#include "IpAddress.h" + +namespace pcpp +{ + namespace internal + { + std::unique_ptr getAllLocalPcapDevices() + { + pcap_if_t* interfaceListRaw; + std::array errbuf; + int err = pcap_findalldevs(&interfaceListRaw, errbuf.data()); + if (err < 0) + { + throw std::runtime_error("Error searching for devices: " + std::string(errbuf.begin(), errbuf.end())); + } + // Assigns the raw pointer to the smart pointer with specialized deleter. + return std::unique_ptr(interfaceListRaw); + } + } +} diff --git a/Pcap++/src/DpdkDevice.cpp b/Pcap++/src/DpdkDevice.cpp index f3df12f92e..27e6bec5a7 100644 --- a/Pcap++/src/DpdkDevice.cpp +++ b/Pcap++/src/DpdkDevice.cpp @@ -119,6 +119,7 @@ DpdkDevice::DpdkDevice(int port, uint32_t mBufPoolSize, uint16_t mBufDataSize) std::ostringstream deviceNameStream; deviceNameStream << "DPDK_" << m_Id; m_DeviceName = deviceNameStream.str(); + m_DeviceSocketId = rte_eth_dev_socket_id(m_Id); #if (RTE_VER_YEAR > 19) || (RTE_VER_YEAR == 19 && RTE_VER_MONTH >= 8) struct rte_ether_addr etherAddr; @@ -364,7 +365,7 @@ bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesTo for (uint8_t i = 0; i < numOfRxQueuesToInit; i++) { int ret = rte_eth_rx_queue_setup((uint8_t) m_Id, i, - m_Config.receiveDescriptorsNumber, 0, + m_Config.receiveDescriptorsNumber, m_DeviceSocketId, NULL, m_MBufMempool); if (ret < 0) @@ -380,7 +381,7 @@ bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesTo { int ret = rte_eth_tx_queue_setup((uint8_t) m_Id, i, m_Config.transmitDescriptorsNumber, - 0, NULL); + m_DeviceSocketId, NULL); if (ret < 0) { PCPP_LOG_ERROR("Failed to init TX queue #" << i << " for port " << m_Id << ". Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); @@ -400,7 +401,7 @@ bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesTo for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) { - m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, rte_eth_dev_socket_id(m_Id)); + m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, m_DeviceSocketId); if (m_TxBuffers[i] == NULL) { @@ -432,7 +433,7 @@ bool DpdkDevice::initMemPool(struct rte_mempool*& memPool, const char* mempoolNa bool ret = false; // create mbuf pool - memPool = rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, m_MBufDataSize, rte_socket_id()); + memPool = rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, m_MBufDataSize, m_DeviceSocketId); if (memPool == NULL) { PCPP_LOG_ERROR("Failed to create packets memory pool for port " << m_Id << ", pool name: " << mempoolName << ". Error was: '" << rte_strerror(rte_errno) << "' [Error code: " << rte_errno << "]"); diff --git a/Pcap++/src/DpdkDeviceList.cpp b/Pcap++/src/DpdkDeviceList.cpp index 7d1a4ddd76..f2b139129b 100644 --- a/Pcap++/src/DpdkDeviceList.cpp +++ b/Pcap++/src/DpdkDeviceList.cpp @@ -73,7 +73,7 @@ DpdkDeviceList::~DpdkDeviceList() m_DpdkDeviceList.clear(); } -bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint16_t mBufDataSize, uint8_t masterCore, uint32_t initDpdkArgc, char **initDpdkArgv, const std::string& appName) +bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint16_t mBufDataSize, uint8_t masterCore, uint32_t initDpdkArgc, char **initDpdkArgv, const std::string& appName, bool verifyHugePagesAndDriver) { char **initDpdkArgvBuffer; @@ -88,7 +88,7 @@ bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, } } - if (!verifyHugePagesAndDpdkDriver()) + if (verifyHugePagesAndDriver && !verifyHugePagesAndDpdkDriver()) { return false; } @@ -258,24 +258,25 @@ bool DpdkDeviceList::verifyHugePagesAndDpdkDriver() execResult = executeShellCommand("lsmod | grep -s igb_uio"); if (execResult == "") { - execResult = executeShellCommand("modinfo -d uio_pci_generic"); - if (execResult.find("ERROR") != std::string::npos) + try { - execResult = executeShellCommand("modinfo -d vfio-pci"); - if (execResult.find("ERROR") != std::string::npos) + execResult = executeShellCommand("modinfo -d uio_pci_generic"); + PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); + } + catch (const std::runtime_error&) + { + try { - PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel modules are loaded so DPDK cannot be initialized. Please run /setup_dpdk.sh"); - return false; + execResult = executeShellCommand("modinfo -d vfio-pci"); + PCPP_LOG_DEBUG("vfio-pci module is loaded"); } - else + catch (const std::runtime_error&) { - PCPP_LOG_DEBUG("vfio-pci module is loaded"); + PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel modules are loaded so DPDK cannot be " + "initialized. Please run /setup_dpdk.sh"); + return false; } } - else - { - PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); - } } else PCPP_LOG_DEBUG("igb_uio driver is loaded"); diff --git a/Pcap++/src/PcapFileDevice.cpp b/Pcap++/src/PcapFileDevice.cpp index b911bb7c8f..82e1f59870 100644 --- a/Pcap++/src/PcapFileDevice.cpp +++ b/Pcap++/src/PcapFileDevice.cpp @@ -36,6 +36,18 @@ struct packet_header uint32_t len; }; +static bool checkNanoSupport() +{ +#if defined(PCAP_TSTAMP_PRECISION_NANO) + return true; +#else + PCPP_LOG_DEBUG( + "PcapPlusPlus was compiled without nano precision support which requires libpcap > 1.5.1. Please " + "recompile PcapPlusPlus with nano precision support to use this feature. Using default microsecond precision"); + return false; +#endif +} + // ~~~~~~~~~~~~~~~~~~~ // IFileDevice members // ~~~~~~~~~~~~~~~~~~~ @@ -275,11 +287,23 @@ bool PcapFileReaderDevice::open() m_PcapLinkLayerType = static_cast(linkLayer); - PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); +#if defined(PCAP_TSTAMP_PRECISION_NANO) + m_Precision = static_cast(pcap_get_tstamp_precision(m_PcapDescriptor)); + std::string precisionStr = (m_Precision == FileTimestampPrecision::Nanoseconds) ? "nanoseconds" : "microseconds"; +#else + m_Precision = FileTimestampPrecision::Microseconds; + std::string precisionStr = "microseconds"; +#endif + PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "' with precision " << precisionStr); m_DeviceOpened = true; return true; } +bool PcapFileReaderDevice::isNanoSecondPrecisionSupported() +{ + return checkNanoSupport(); +} + void PcapFileReaderDevice::getStatistics(PcapStats& stats) const { stats.packetsRecv = m_NumOfPacketsRead; @@ -528,13 +552,24 @@ IFileWriterDevice:: IFileWriterDevice(const std::string& fileName) : IFileDevice // PcapFileWriterDevice members // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType) : IFileWriterDevice(fileName) +PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType, bool nanosecondsPrecision) : IFileWriterDevice(fileName) { m_PcapDumpHandler = nullptr; m_NumOfPacketsNotWritten = 0; m_NumOfPacketsWritten = 0; m_PcapLinkLayerType = linkLayerType; m_AppendMode = false; +#if defined(PCAP_TSTAMP_PRECISION_NANO) + m_Precision = nanosecondsPrecision ? FileTimestampPrecision::Nanoseconds : FileTimestampPrecision::Microseconds; +#else + if (nanosecondsPrecision) + { + PCPP_LOG_ERROR( + "PcapPlusPlus was compiled without nano precision support which requires libpcap > 1.5.1. Please " + "recompile PcapPlusPlus with nano precision support to use this feature. Using default microsecond precision"); + } + m_Precision = FileTimestampPrecision::Microseconds; +#endif m_File = nullptr; } @@ -567,7 +602,19 @@ bool PcapFileWriterDevice::writePacket(RawPacket const& packet) pktHdr.caplen = ((RawPacket&)packet).getRawDataLen(); pktHdr.len = ((RawPacket&)packet).getFrameLength(); timespec packet_timestamp = ((RawPacket&)packet).getPacketTimeStamp(); +#if defined(PCAP_TSTAMP_PRECISION_NANO) + if (m_Precision != FileTimestampPrecision::Nanoseconds) + { + TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); + } + else + { + pktHdr.ts.tv_sec = packet_timestamp.tv_sec; + pktHdr.ts.tv_usec = packet_timestamp.tv_nsec; + } +#else TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); +#endif if (!m_AppendMode) pcap_dump((uint8_t*)m_PcapDumpHandler, &pktHdr, ((RawPacket&)packet).getRawData()); else @@ -604,6 +651,11 @@ bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets) return true; } +bool PcapFileWriterDevice::isNanoSecondPrecisionSupported() +{ + return checkNanoSupport(); +} + bool PcapFileWriterDevice::open() { if (m_PcapDescriptor != nullptr) @@ -625,7 +677,11 @@ bool PcapFileWriterDevice::open() m_NumOfPacketsNotWritten = 0; m_NumOfPacketsWritten = 0; +#if defined(PCAP_TSTAMP_PRECISION_NANO) + m_PcapDescriptor = pcap_open_dead_with_tstamp_precision(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE, static_cast(m_Precision)); +#else m_PcapDescriptor = pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE); +#endif if (m_PcapDescriptor == nullptr) { PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_open_dead returned NULL"); diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 50070c6074..eb850077bf 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -3,7 +3,9 @@ #include "PcapFilter.h" #include "Logger.h" #include "IPv4Layer.h" +#include "PcapUtils.h" #include +#include #if defined(_WIN32) #include #endif @@ -27,15 +29,23 @@ bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) return m_BpfWrapper.matchPacketWithFilter(rawPacket); } -BpfFilterWrapper::BpfFilterWrapper() +namespace internal { - m_Program = nullptr; - m_LinkType = LINKTYPE_ETHERNET; + void BpfProgramDeleter::operator()(bpf_program* ptr) const + { + pcap_freecode(ptr); + delete ptr; + } } -BpfFilterWrapper::~BpfFilterWrapper() +BpfFilterWrapper::BpfFilterWrapper() : m_LinkType(LinkLayerType::LINKTYPE_ETHERNET) {} + +BpfFilterWrapper::BpfFilterWrapper(const BpfFilterWrapper& other) : BpfFilterWrapper() { setFilter(other.m_FilterStr, other.m_LinkType); } + +BpfFilterWrapper& BpfFilterWrapper::operator=(const BpfFilterWrapper &other) { - freeProgram(); + setFilter(other.m_FilterStr, other.m_LinkType); + return *this; } bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkType) @@ -48,23 +58,21 @@ bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkTy if (filter != m_FilterStr || linkType != m_LinkType) { - pcap_t* pcap = pcap_open_dead(linkType, DEFAULT_SNAPLEN); + auto pcap = std::unique_ptr(pcap_open_dead(linkType, DEFAULT_SNAPLEN)); if (pcap == nullptr) { return false; } - bpf_program* newProg = new bpf_program; - int ret = pcap_compile(pcap, newProg, filter.c_str(), 1, 0); - pcap_close(pcap); + std::unique_ptr newProg = std::unique_ptr(new bpf_program); + int ret = pcap_compile(pcap.get(), newProg.get(), filter.c_str(), 1, 0); if (ret < 0) { - delete newProg; return false; } - freeProgram(); - m_Program = newProg; + // Reassigns ownership of the bpf program to a new unique_ptr with a custom deleter as it now requires specialized cleanup. + m_Program = std::unique_ptr(newProg.release()); m_FilterStr = filter; m_LinkType = linkType; } @@ -74,13 +82,8 @@ bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkTy void BpfFilterWrapper::freeProgram() { - if (m_Program != nullptr) - { - pcap_freecode(m_Program); - delete m_Program; - m_Program = nullptr; - m_FilterStr.clear(); - } + m_Program = nullptr; + m_FilterStr.clear(); } bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) @@ -103,7 +106,7 @@ bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t pktHdr.len = packetDataLength; TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packetTimestamp); - return (pcap_offline_filter(m_Program, &pktHdr, packetData) != 0); + return (pcap_offline_filter(m_Program.get(), &pktHdr, packetData) != 0); } void BPFStringFilter::parseToString(std::string& result) @@ -153,93 +156,20 @@ std::string IFilterWithOperator::parseOperator() } } -void IPFilter::convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const -{ - if (m_IPv4Mask.empty()) - return; - - // Handle the mask - - // The following code lines verify both ipAddress and ipv4Mask are valid IPv4 addresses - // The IPv4 limitation comes from the fact libPcap/WinPcap/Npcap doesn't support mask for IPv6 addresses - - IPv4Address ipAddr; - try - { - ipAddr = IPv4Address(m_Address); - } - catch(const std::exception&) - { - PCPP_LOG_ERROR("Invalid IP address '" << m_Address << "', setting the mask to an empty value"); - mask.clear(); - return; - } - - IPv4Address maskAsAddr; - try - { - maskAsAddr = IPv4Address(m_IPv4Mask); - } - catch(const std::exception&) - { - PCPP_LOG_ERROR("Invalid IPv4 mask '" << m_IPv4Mask << "', setting the mask to an empty value"); - mask.clear(); - return; - } - - // If all addresses are IPv4 valid addresses, make sure ipAddress matches the mask. If it's not, mask the address with the mask - // The reason for doing that is libPcap/WinPcap/Npcap doesn't allow filtering an IP address that doesn't match the mask - - uint32_t addrAsIntAfterMask = ipAddr.toInt() & maskAsAddr.toInt(); - ipAddrmodified = IPv4Address(addrAsIntAfterMask).toString(); -} - -void IPFilter::convertToIPAddressWithLen(std::string& ipAddrmodified) const -{ - if (m_Len == 0) - return; - - // Handle the length - - // The following code lines verify IP address is valid (IPv4 or IPv6) - - IPAddress ipAddr; - try - { - ipAddr = IPAddress(ipAddrmodified); - } - catch(const std::exception&) - { - PCPP_LOG_ERROR("Invalid IP address '" << ipAddrmodified << "', setting len to zero"); - return; - } - - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - uint32_t addrAsInt = ipAddr.getIPv4().toInt(); - uint32_t mask = static_cast(-1) >> ((sizeof(uint32_t) * 8) - m_Len); - addrAsInt &= mask; - ipAddrmodified = IPv4Address(addrAsInt).toString(); - } -} - void IPFilter::parseToString(std::string& result) { std::string dir; - std::string ipAddr = m_Address; - std::string mask = m_IPv4Mask; - convertToIPAddressWithMask(ipAddr, mask); - convertToIPAddressWithLen(ipAddr); + std::string ipAddr = m_Network.toString(); + std::string ipProto = m_Network.isIPv6Network() ? "ip6" : "ip"; + parseDirection(dir); - result = "ip and " + dir + " net " + ipAddr; - if (m_IPv4Mask != "") - result += " mask " + mask; - else if (m_Len > 0) - { - std::ostringstream stream; - stream << m_Len; - result += '/' + stream.str(); - } + + result.reserve(ipProto.size() + dir.size() + ipAddr.size() + 10 /* Hard-coded strings */); + result = ipProto; + result += " and "; + result += dir; + result += " net "; + result += ipAddr; } void IPv4IDFilter::parseToString(std::string& result) diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 70a0c18ab7..c3a690bd95 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -1,6 +1,8 @@ #define LOG_MODULE PcapLogModuleLiveDevice #include "IpUtils.h" +#include "DeviceUtils.h" +#include "PcapUtils.h" #include "PcapLiveDevice.h" #include "PcapLiveDeviceList.h" #include "Packet.h" @@ -11,11 +13,13 @@ #include #include "Logger.h" #include "SystemUtils.h" -#include +#include #include #include #include #include +#include +#include #if defined(_WIN32) // The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap there are // compilation errors due to struct redefinition when including both Packet32.h and pcap.h @@ -74,7 +78,7 @@ static pcap_direction_t directionTypeMap(PcapLiveDevice::PcapDirection direction PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : - IPcapDevice(), m_PcapSelectableFd(-1), m_DefaultGateway(IPv4Address::Zero), m_UsePoll(false) + IPcapDevice(), m_PcapSendDescriptor(nullptr), m_PcapSelectableFd(-1), m_DefaultGateway(IPv4Address::Zero), m_UsePoll(false) { m_DeviceMtu = 0; m_LinkType = LINKTYPE_ETHERNET; @@ -92,9 +96,9 @@ PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool ca pInterface->addresses = pInterface->addresses->next; if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && pInterface->addresses != nullptr && pInterface->addresses->addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(pInterface->addresses->addr, addrAsString); - PCPP_LOG_DEBUG(" " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(pInterface->addresses->addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG(" " << addrAsString.data()); } } @@ -134,7 +138,7 @@ PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool ca void PcapLiveDevice::onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) { - PcapLiveDevice* pThis = (PcapLiveDevice*)user; + PcapLiveDevice* pThis = reinterpret_cast(user); if (pThis == nullptr) { PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); @@ -149,7 +153,7 @@ void PcapLiveDevice::onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pk void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) { - PcapLiveDevice* pThis = (PcapLiveDevice*)user; + PcapLiveDevice* pThis = reinterpret_cast(user); if (pThis == nullptr) { PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); @@ -164,7 +168,7 @@ void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, const struct pcap_ void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) { - PcapLiveDevice* pThis = (PcapLiveDevice*)user; + PcapLiveDevice* pThis = reinterpret_cast(user); if (pThis == nullptr) { PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); @@ -401,34 +405,34 @@ void PcapLiveDevice::close() PCPP_LOG_DEBUG("Device '" << m_Name << "' closed"); } -PcapLiveDevice* PcapLiveDevice::clone() +PcapLiveDevice* PcapLiveDevice::clone() const { - PcapLiveDevice *retval = nullptr; - - pcap_if_t *interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) + std::unique_ptr interfaceList; + try + { + interfaceList = internal::getAllLocalPcapDevices(); + } + catch (const std::exception& e) { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + PCPP_LOG_ERROR(e.what()); return nullptr; } - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { - if(!strcmp(currInterface->name, getName().c_str())) - break; - currInterface = currInterface->next; + if (!std::strcmp(currInterface->name, getName().c_str())) + { + return cloneInternal(*currInterface); + } } - if(currInterface) - retval = new PcapLiveDevice(currInterface, true, true, true); - else - PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); + PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); + return nullptr; +} - pcap_freealldevs(interfaceList); - return retval; +PcapLiveDevice* PcapLiveDevice::cloneInternal(pcap_if_t& devInterface) const +{ + return new PcapLiveDevice(&devInterface, true, true, true); } bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie) @@ -671,9 +675,9 @@ void PcapLiveDevice::getStatistics(PcapStats& stats) const stats.packetsDropByInterface = pcapStats.ps_ifdrop; } -bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) +bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) const { - if (packetPayloadLength > (int)m_DeviceMtu) + if (packetPayloadLength > static_cast(m_DeviceMtu)) { PCPP_LOG_ERROR("Payload length [" << packetPayloadLength << "] is larger than device MTU [" << m_DeviceMtu << "]"); return false; @@ -685,12 +689,12 @@ bool PcapLiveDevice::sendPacket(RawPacket const& rawPacket, bool checkMtu) { if (checkMtu) { - RawPacket *rPacket = (RawPacket *)&rawPacket; + RawPacket* rPacket = const_cast(&rawPacket); Packet parsedPacket = Packet(rPacket, OsiModelDataLinkLayer); return sendPacket(&parsedPacket, true); } // Send packet without Mtu check - return sendPacket(((RawPacket&)rawPacket).getRawData(), ((RawPacket&)rawPacket).getRawDataLen()); + return sendPacket(rawPacket.getRawData(), rawPacket.getRawDataLen()); } bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, int packetPayloadLength) @@ -740,10 +744,10 @@ bool PcapLiveDevice::sendPacket(Packet* packet, bool checkMtu) switch (packet->getFirstLayer()->getOsiModelLayer()) { case (pcpp::OsiModelDataLinkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getLayerPayloadSize(); + packetPayloadLength = static_cast(packet->getFirstLayer()->getLayerPayloadSize()); break; case (pcpp::OsiModelNetworkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getDataLen(); + packetPayloadLength = static_cast(packet->getFirstLayer()->getDataLen()); break; default: // if packet layer is not known, do not perform MTU check. @@ -805,7 +809,7 @@ void PcapLiveDevice::setDeviceMtu() } uint32_t mtuValue = 0; - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + LPADAPTER adapter = PacketOpenAdapter(const_cast(m_Name.c_str())); if (adapter == NULL) { PCPP_LOG_ERROR("Error in retrieving MTU: Adapter is NULL"); @@ -813,7 +817,7 @@ void PcapLiveDevice::setDeviceMtu() } uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + PACKET_OID_DATA* oidData = reinterpret_cast(buffer); oidData->Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; oidData->Length = sizeof(uint32_t); memcpy(oidData->Data, &mtuValue, sizeof(uint32_t)); @@ -879,7 +883,7 @@ void PcapLiveDevice::setDeviceMacAddress() { #if defined(_WIN32) - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + LPADAPTER adapter = PacketOpenAdapter(const_cast(m_Name.c_str())); if (adapter == NULL) { PCPP_LOG_ERROR("Error in retrieving MAC address: Adapter is NULL"); @@ -887,7 +891,7 @@ void PcapLiveDevice::setDeviceMacAddress() } uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + PACKET_OID_DATA* oidData = reinterpret_cast(buffer); oidData->Oid = OID_802_3_CURRENT_ADDRESS; oidData->Length = 6; oidData->Data[0] = 0; @@ -982,16 +986,18 @@ void PcapLiveDevice::setDefaultGateway() { #if defined(_WIN32) ULONG outBufLen = sizeof (IP_ADAPTER_INFO); - uint8_t* buffer = new uint8_t[outBufLen]; - PIP_ADAPTER_INFO adapterInfo = (IP_ADAPTER_INFO*)buffer; + std::vector buffer(outBufLen); + PIP_ADAPTER_INFO adapterInfo = reinterpret_cast(buffer.data()); DWORD retVal = 0; retVal = GetAdaptersInfo(adapterInfo, &outBufLen); - uint8_t* buffer2 = new uint8_t[outBufLen]; if (retVal == ERROR_BUFFER_OVERFLOW) - adapterInfo = (IP_ADAPTER_INFO *)buffer2; - - retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + { + buffer.resize(outBufLen); + // Repins the adapter info pointer to the vector data pointer as the vector might be reallocated during the resize. + adapterInfo = reinterpret_cast(buffer.data()); + retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + } if (retVal == NO_ERROR) { @@ -1018,10 +1024,6 @@ void PcapLiveDevice::setDefaultGateway() { PCPP_LOG_ERROR("Error retrieving default gateway address"); } - - delete[] buffer; - // cppcheck-suppress uninitdata - delete[] buffer2; #elif defined(__linux__) std::ifstream routeFile("/proc/net/route"); std::string line; @@ -1086,16 +1088,16 @@ void PcapLiveDevice::setDefaultGateway() IPv4Address PcapLiveDevice::getIPv4Address() const { - for(const auto &addrIter : m_Addresses) + for(const auto& addrIter : m_Addresses) { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is NULL"); @@ -1117,15 +1119,15 @@ IPv4Address PcapLiveDevice::getIPv4Address() const IPv6Address PcapLiveDevice::getIPv6Address() const { - for (const auto &addrIter : m_Addresses) + for (const auto& addrIter : m_Addresses) { if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr *currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr *currAddr = internal::try_sockaddr2in6_addr(addrIter.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is NULL"); diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index 76a4417913..5d0650d731 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -1,10 +1,14 @@ #define LOG_MODULE PcapLogModuleLiveDevice #include "IpUtils.h" +#include "IpAddressUtils.h" #include "PcapLiveDeviceList.h" #include "Logger.h" +#include "PcapUtils.h" +#include "DeviceUtils.h" #include "SystemUtils.h" #include "pcap.h" +#include #include #include #include @@ -28,64 +32,64 @@ PcapLiveDeviceList::PcapLiveDeviceList() init(); } -PcapLiveDeviceList::~PcapLiveDeviceList() +void PcapLiveDeviceList::init() { - for(const auto &devIter : m_LiveDeviceList) + std::unique_ptr interfaceList; + try { - delete devIter; + interfaceList = internal::getAllLocalPcapDevices(); } -} - -void PcapLiveDeviceList::init() -{ - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) + catch (const std::exception& e) { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + PCPP_LOG_ERROR(e.what()); } PCPP_LOG_DEBUG("Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) + + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { #if defined(_WIN32) - PcapLiveDevice* dev = new WinPcapLiveDevice(currInterface, true, true, true); + auto dev = std::unique_ptr(new WinPcapLiveDevice(currInterface, true, true, true)); #else //__linux__, __APPLE__, __FreeBSD__ - PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); + auto dev = std::unique_ptr(new PcapLiveDevice(currInterface, true, true, true)); #endif - currInterface = currInterface->next; - m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); + m_LiveDeviceList.push_back(std::move(dev)); } - setDnsServers(); + m_LiveDeviceListView.resize(m_LiveDeviceList.size()); + // Full update of all elements of the view vector to synchronize them with the main vector. + std::transform(m_LiveDeviceList.begin(), m_LiveDeviceList.end(), m_LiveDeviceListView.begin(), + [](const std::unique_ptr& ptr) { return ptr.get(); }); - PCPP_LOG_DEBUG("Freeing live device data"); - pcap_freealldevs(interfaceList); + setDnsServers(); } void PcapLiveDeviceList::setDnsServers() { #if defined(_WIN32) - FIXED_INFO * fixedInfo; + FIXED_INFO* fixedInfo; ULONG ulOutBufLen; DWORD dwRetVal; - IP_ADDR_STRING * pIPAddr; + IP_ADDR_STRING* pIPAddr; - uint8_t buf1[sizeof(FIXED_INFO)]; - fixedInfo = (FIXED_INFO *) buf1; - ulOutBufLen = sizeof( FIXED_INFO ); + std::array bufferOnStack; + fixedInfo = reinterpret_cast(bufferOnStack.data()); + ulOutBufLen = bufferOnStack.size(); dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen ); - uint8_t* buf2 = new uint8_t[ulOutBufLen]; + std::vector bufferOnHeap; if(ERROR_BUFFER_OVERFLOW == dwRetVal) { - fixedInfo = (FIXED_INFO *)buf2; + // Stack buffer was not enough. Allocating a heap buffer. + bufferOnHeap.resize(ulOutBufLen); + fixedInfo = reinterpret_cast(bufferOnHeap.data()); + ulOutBufLen = bufferOnHeap.size(); + // Retrying to get network info. + dwRetVal = GetNetworkParams(fixedInfo, &ulOutBufLen); } - if ((dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen )) != 0) + if (dwRetVal != 0) PCPP_LOG_ERROR("Call to GetNetworkParams failed. Return Value: " << std::hex << dwRetVal); else { @@ -115,8 +119,6 @@ void PcapLiveDeviceList::setDnsServers() pIPAddr = pIPAddr -> Next; } } - - delete[] buf2; #elif defined(__linux__) // verify that nmcli exist std::string command = "command -v nmcli >/dev/null 2>&1 || { echo 'nmcli not installed'; }"; @@ -235,7 +237,7 @@ void PcapLiveDeviceList::setDnsServers() sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; if (saddr == nullptr) continue; - in_addr* inaddr = internal::sockaddr2in_addr(saddr); + in_addr* inaddr = internal::try_sockaddr2in_addr(saddr); if (inaddr == nullptr) continue; @@ -267,29 +269,29 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPAddress& ipAdd PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const { PCPP_LOG_DEBUG("Searching all live devices..."); - for(const auto &devIter : m_LiveDeviceList) + for(const auto& devicePtr : m_LiveDeviceList) { - PCPP_LOG_DEBUG("Searching device '" << devIter->m_Name << "'. Searching all addresses..."); - for(const auto &addrIter : devIter->m_Addresses) + PCPP_LOG_DEBUG("Searching device '" << devicePtr->m_Name << "'. Searching all addresses..."); + for(const auto& addressInfo : devicePtr->m_Addresses) { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addressInfo.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addressInfo.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addressInfo.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is nullptr"); continue; } - if (currAddr->s_addr == ipAddr.toInt()) + if (*currAddr == ipAddr) { PCPP_LOG_DEBUG("Found matched address!"); - return devIter; + return devicePtr.get(); } } } @@ -300,35 +302,30 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipA PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const { PCPP_LOG_DEBUG("Searching all live devices..."); - for(const auto &devIter : m_LiveDeviceList) + for(const auto& devicePtr : m_LiveDeviceList) { - PCPP_LOG_DEBUG("Searching device '" << devIter->m_Name << "'. Searching all addresses..."); - for(const auto &addrIter : devIter->m_Addresses) + PCPP_LOG_DEBUG("Searching device '" << devicePtr->m_Name << "'. Searching all addresses..."); + for(const auto& addressInfo : devicePtr->m_Addresses) { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter.addr != nullptr) + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addressInfo.addr != nullptr) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addressInfo.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr* currAddr = internal::try_sockaddr2in6_addr(addressInfo.addr); if (currAddr == nullptr) { PCPP_LOG_DEBUG("Address is nullptr"); continue; } - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) + if (*currAddr == ip6Addr) { PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; - return devIter; + return devicePtr.get(); } - - delete [] addrAsArr; } } @@ -357,7 +354,7 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& n { PCPP_LOG_DEBUG("Searching all live devices..."); auto devIter = std::find_if(m_LiveDeviceList.begin(), m_LiveDeviceList.end(), - [&name](const PcapLiveDevice *dev) { return dev->getName() == name; }); + [&name](const std::unique_ptr& dev) { return dev->getName() == name; }); if (devIter == m_LiveDeviceList.end()) { @@ -365,7 +362,7 @@ PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& n return nullptr; } - return *devIter; + return devIter->get(); } PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const @@ -388,11 +385,7 @@ PcapLiveDeviceList* PcapLiveDeviceList::clone() void PcapLiveDeviceList::reset() { - for(auto devIter : m_LiveDeviceList) - { - delete devIter; - } - + m_LiveDeviceListView.clear(); m_LiveDeviceList.clear(); m_DnsServers.clear(); diff --git a/Pcap++/src/PcapRemoteDevice.cpp b/Pcap++/src/PcapRemoteDevice.cpp index b08733f10c..1ee9b24060 100644 --- a/Pcap++/src/PcapRemoteDevice.cpp +++ b/Pcap++/src/PcapRemoteDevice.cpp @@ -14,19 +14,19 @@ pcap_rmtauth PcapRemoteAuthentication::getPcapRmAuth() const { pcap_rmtauth result; result.type = RPCAP_RMTAUTH_PWD; - result.username = (char*)userName.c_str(); - result.password = (char*)password.c_str(); + result.username = const_cast(userName.c_str()); + result.password = const_cast(password.c_str()); return result; } -PcapRemoteDevice::PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) +PcapRemoteDevice::PcapRemoteDevice(pcap_if_t* iface, std::shared_ptr remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) : PcapLiveDevice(iface, false, false, false) + , m_RemoteMachineIpAddress(remoteMachineIP) + , m_RemoteMachinePort(remoteMachinePort) + , m_RemoteAuthentication(std::move(remoteAuthentication)) { PCPP_LOG_DEBUG("MTU calculation isn't supported for remote devices. Setting MTU to 1514"); m_DeviceMtu = 1514; - m_RemoteMachineIpAddress = remoteMachineIP; - m_RemoteMachinePort = remoteMachinePort; - m_RemoteAuthentication = remoteAuthentication; } @@ -35,16 +35,16 @@ bool PcapRemoteDevice::open() char errbuf[PCAP_ERRBUF_SIZE]; int flags = PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_RPCAP; //PCAP_OPENFLAG_DATATX_UDP doesn't always work PCPP_LOG_DEBUG("Opening device '" << m_Name << "'"); - pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth* pRmAuth = nullptr; pcap_rmtauth rmAuth; - if (m_RemoteAuthentication != NULL) + if (m_RemoteAuthentication != nullptr) { rmAuth = m_RemoteAuthentication->getPcapRmAuth(); pRmAuth = &rmAuth; } m_PcapDescriptor = pcap_open(m_Name.c_str(), PCPP_MAX_PACKET_SIZE, flags, 250, pRmAuth, errbuf); - if (m_PcapDescriptor == NULL) + if (m_PcapDescriptor == nullptr) { PCPP_LOG_ERROR("Error opening device. Error was: " << errbuf); m_DeviceOpened = false; @@ -70,10 +70,10 @@ bool PcapRemoteDevice::open() return true; } -void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) +void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void* ptr) { - PcapRemoteDevice* pThis = (PcapRemoteDevice*)ptr; - if (pThis == NULL) + PcapRemoteDevice* pThis = static_cast(ptr); + if (pThis == nullptr) { PCPP_LOG_ERROR("Capture thread: Unable to extract PcapLiveDevice instance"); return 0; @@ -89,7 +89,7 @@ void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) while (!pThis->m_StopThread) { if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrives((uint8_t*)pThis, pkthdr, pktData); + onPacketArrives(reinterpret_cast(pThis), pkthdr, pktData); } } else @@ -97,7 +97,7 @@ void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) while (!pThis->m_StopThread) { if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrivesNoCallback((uint8_t*)pThis, pkthdr, pktData); + onPacketArrivesNoCallback(reinterpret_cast(pThis), pkthdr, pktData); } } PCPP_LOG_DEBUG("Ended capture thread for device '" << pThis->m_Name << "'"); @@ -113,13 +113,13 @@ void PcapRemoteDevice::getStatistics(PcapStats& stats) const { int allocatedMemory; pcap_stat* tempStats = pcap_stats_ex(m_PcapDescriptor, &allocatedMemory); - if (allocatedMemory < (int)sizeof(pcap_stat)) + if (allocatedMemory < static_cast(sizeof(pcap_stat))) { PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "': WinPcap did not allocate the entire struct"); return; } stats.packetsRecv = tempStats->ps_capt; - stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; + stats.packetsDrop = static_cast(tempStats->ps_drop) + tempStats->ps_netdrop; stats.packetsDropByInterface = tempStats->ps_ifdrop; } diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index 7cff5e73ba..e852e6c297 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -5,12 +5,49 @@ #include "PcapRemoteDeviceList.h" #include "Logger.h" #include "IpUtils.h" +#include "PcapUtils.h" +#include "IpAddressUtils.h" #include "pcap.h" +#include #include namespace pcpp { + namespace + { + /** + * Fetches a list of all network devices on a remote machine that WinPcap/NPcap can find. + * @param[in] ipAddress IP address of the remote machine. + * @param[in] port Port to use when connecting to the remote machine. + * @param[in] pRmAuth Pointer to an authentication structure to use when connecting to the remote machine. Nullptr if no authentication is required. + * @return A smart pointer to an interface list structure. + * @throws std::runtime_error The system encountered an error fetching the devices. + */ + std::unique_ptr getAllRemotePcapDevices(const IPAddress& ipAddress, uint16_t port, pcap_rmtauth* pRmAuth = nullptr) + { + PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); + std::array remoteCaptureString; + std::array errorBuf; + if (pcap_createsrcstr(remoteCaptureString.data(), PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), + std::to_string(port).c_str(), nullptr, errorBuf.data()) != 0) + { + throw std::runtime_error("Error creating the remote connection string. Error: " + + std::string(errorBuf.begin(), errorBuf.end())); + } + + PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString.data()); + + pcap_if_t* interfaceListRaw; + if (pcap_findalldevs_ex(remoteCaptureString.data(), pRmAuth, &interfaceListRaw, errorBuf.data()) < 0) + { + throw std::runtime_error("Error retrieving device on remote machine. Error: " + + std::string(errorBuf.begin(), errorBuf.end())); + } + return std::unique_ptr(interfaceListRaw); + } + } + PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port) { return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); @@ -18,34 +55,24 @@ PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth) { - PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); - char remoteCaptureString[PCAP_BUF_SIZE]; - char errbuf[PCAP_ERRBUF_SIZE]; - std::ostringstream portAsString; - portAsString << port; - if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), portAsString.str().c_str(), NULL, errbuf) != 0) - { - PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " << errbuf); - return NULL; - } - - PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); - - pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth* pRmAuth = nullptr; pcap_rmtauth rmAuth; - if (remoteAuth != NULL) + if (remoteAuth != nullptr) { PCPP_LOG_DEBUG("Authentication requested. Username: " << remoteAuth->userName << ", Password: " << remoteAuth->password); rmAuth = remoteAuth->getPcapRmAuth(); pRmAuth = &rmAuth; } - pcap_if_t* interfaceList; - char errorBuf[PCAP_ERRBUF_SIZE]; - if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, errorBuf) < 0) + std::unique_ptr interfaceList; + try { - PCPP_LOG_ERROR("Error retrieving device on remote machine. Error string is: " << errorBuf); - return NULL; + interfaceList = getAllRemotePcapDevices(ipAddress, port, pRmAuth); + } + catch (const std::exception& e) + { + PCPP_LOG_ERROR(e.what()); + return nullptr; } PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); @@ -53,16 +80,14 @@ PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& resultList->setRemoteMachinePort(port); resultList->setRemoteAuthentication(remoteAuth); - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) + + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { PcapRemoteDevice* pNewRemoteDevice = new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, resultList->getRemoteMachineIpAddress(), resultList->getRemoteMachinePort()); resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); - currInterface = currInterface->next; } - pcap_freealldevs(interfaceList); return resultList; } @@ -107,19 +132,19 @@ PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& i { if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter.addr != NULL) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in_addr* currAddr = internal::sockaddr2in_addr(addrIter.addr); + in_addr* currAddr = internal::try_sockaddr2in_addr(addrIter.addr); if (currAddr == NULL) { PCPP_LOG_DEBUG("Address is NULL"); continue; } - if (currAddr->s_addr == ip4Addr.toInt()) + if (*currAddr == ip4Addr) { PCPP_LOG_DEBUG("Found matched address!"); return (*devIter); @@ -141,27 +166,23 @@ PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& i { if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter.addr != NULL) { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter.addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); + std::array addrAsString; + internal::sockaddr2string(addrIter.addr, addrAsString.data(), addrAsString.size()); + PCPP_LOG_DEBUG("Searching address " << addrAsString.data()); } - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter.addr); + in6_addr* currAddr = internal::try_sockaddr2in6_addr(addrIter.addr); if (currAddr == NULL) { PCPP_LOG_DEBUG("Address is NULL"); continue; } - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) + if (*currAddr == ip6Addr) { PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; return (*devIter); } - delete [] addrAsArr; } } @@ -181,13 +202,11 @@ void PcapRemoteDeviceList::setRemoteMachinePort(uint16_t port) void PcapRemoteDeviceList::setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth) { - if (remoteAuth != NULL) - m_RemoteAuthentication = new PcapRemoteAuthentication(*remoteAuth); + if (remoteAuth != nullptr) + m_RemoteAuthentication = std::shared_ptr(new PcapRemoteAuthentication(*remoteAuth)); else { - if (m_RemoteAuthentication != NULL) - delete m_RemoteAuthentication; - m_RemoteAuthentication = NULL; + m_RemoteAuthentication = nullptr; } } @@ -199,11 +218,6 @@ PcapRemoteDeviceList::~PcapRemoteDeviceList() delete (*devIter); m_RemoteDeviceList.erase(devIter); } - - if (m_RemoteAuthentication != NULL) - { - delete m_RemoteAuthentication; - } } } // namespace pcpp diff --git a/Pcap++/src/PcapUtils.cpp b/Pcap++/src/PcapUtils.cpp new file mode 100644 index 0000000000..b3df4eca44 --- /dev/null +++ b/Pcap++/src/PcapUtils.cpp @@ -0,0 +1,13 @@ +#include "PcapUtils.h" + +#include "pcap.h" + +namespace pcpp +{ + namespace internal + { + void PcapCloseDeleter::operator()(pcap_t* ptr) const { pcap_close(ptr); } + + void PcapFreeAllDevsDeleter::operator()(pcap_if_t* ptr) const { pcap_freealldevs(ptr); } + } +} diff --git a/Pcap++/src/PfRingDeviceList.cpp b/Pcap++/src/PfRingDeviceList.cpp index 8102f901a3..e2f1489c48 100644 --- a/Pcap++/src/PfRingDeviceList.cpp +++ b/Pcap++/src/PfRingDeviceList.cpp @@ -4,21 +4,73 @@ #define LOG_MODULE PcapLogModulePfRingDevice +#include +#include #include "PfRingDeviceList.h" +#include "SystemUtils.h" +#include "DeviceUtils.h" #include "Logger.h" #include "pcap.h" #include "pfring.h" namespace pcpp { + /// @cond PCPP_INTERNAL + + namespace + { + /** + * @class PfRingCloseDeleter + * A deleter that cleans up a pfring structure by calling pfring_close. + */ + struct PfRingCloseDeleter + { + void operator()(pfring* ptr) const { pfring_close(ptr); } + }; + + /** + * Reads the ring version of a PF_RING handle. + * @param[in] ring A PF_RING handle. + * @return A string representation of the ring version or empty string if the read fails. + */ + std::string readPfRingVersion(pfring* ring) + { + uint32_t version; + if (pfring_version(ring, &version) < 0) + { + PCPP_LOG_ERROR("Couldn't retrieve PF_RING version, pfring_version returned an error"); + return {}; + } + + std::array versionAsString; + std::snprintf(versionAsString.data(), versionAsString.size(), "PF_RING v.%u.%u.%u\n", + (version & 0xFFFF0000) >> 16, + (version & 0x0000FF00) >> 8, + version & 0x000000FF); + + return std::string(versionAsString.data()); + } + } + + /// @endcond PfRingDeviceList::PfRingDeviceList() { m_PfRingVersion = ""; - FILE *fd = popen("lsmod | grep pf_ring", "r"); - char buf[16]; - if (!fread(buf, 1, sizeof (buf), fd)) // if there is some result the module must be loaded + bool moduleLoaded = false; + try + { + // if there is some result the module must be loaded + moduleLoaded = !(executeShellCommand("lsmod | grep pf_ring").empty()); + } + catch (const std::exception& e) + { + PCPP_LOG_ERROR("PF_RING load error: " << e.what()); + moduleLoaded = false; + } + + if (!moduleLoaded) { PCPP_LOG_ERROR("PF_RING kernel module isn't loaded. Please run: 'sudo insmod /kernel/pf_ring.ko'"); return; @@ -26,50 +78,46 @@ PfRingDeviceList::PfRingDeviceList() PCPP_LOG_DEBUG("PF_RING kernel module is loaded"); - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; PCPP_LOG_DEBUG("PfRingDeviceList init: searching all interfaces on machine"); - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) + try { - PCPP_LOG_ERROR("Error searching for PF_RING devices: " << errbuf); - } + auto interfaceList = internal::getAllLocalPcapDevices(); - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) - { - uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; - pfring* ring = pfring_open(currInterface->name, 128, flags); - if (ring != NULL) + for (pcap_if_t* currInterface = interfaceList.get(); currInterface != nullptr; currInterface = currInterface->next) { - if (m_PfRingVersion == "") - calcPfRingVersion(ring); - pfring_close(ring); - PfRingDevice* newDev = new PfRingDevice(currInterface->name); - m_PfRingDeviceList.push_back(newDev); - PCPP_LOG_DEBUG("Found interface: " << currInterface->name); + uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; + auto ring = std::unique_ptr(pfring_open(currInterface->name, 128, flags)); + if (ring != nullptr) + { + if (m_PfRingVersion.empty()) + { + m_PfRingVersion = readPfRingVersion(ring.get()); + PCPP_LOG_DEBUG("PF_RING version is: " << m_PfRingVersion); + } + std::unique_ptr newDev = std::unique_ptr(new PfRingDevice(currInterface->name)); + m_PfRingDeviceList.push_back(std::move(newDev)); + PCPP_LOG_DEBUG("Found interface: " << currInterface->name); + } } - - currInterface = currInterface->next; + } + catch (const std::runtime_error& e) + { + PCPP_LOG_ERROR("PfRingDeviceList init error: " << e.what()); } PCPP_LOG_DEBUG("PfRingDeviceList init end"); - pcap_freealldevs(interfaceList); -} -PfRingDeviceList::~PfRingDeviceList() -{ - for(auto devIter : m_PfRingDeviceList) - { - delete devIter; - } + // Full update of all elements of the view vector to synchronize them with the main vector. + m_PfRingDeviceListView.resize(m_PfRingDeviceList.size()); + std::transform(m_PfRingDeviceList.begin(), m_PfRingDeviceList.end(), m_PfRingDeviceListView.begin(), + [](const std::unique_ptr& ptr) { return ptr.get(); }); } PfRingDevice* PfRingDeviceList::getPfRingDeviceByName(const std::string &devName) const { PCPP_LOG_DEBUG("Searching all live devices..."); auto devIter = std::find_if(m_PfRingDeviceList.begin(), m_PfRingDeviceList.end(), - [&devName](const PfRingDevice *dev) { return dev->getDeviceName() == devName; }); + [&devName](const std::unique_ptr& dev) { return dev->getDeviceName() == devName; }); if (devIter == m_PfRingDeviceList.end()) { @@ -77,27 +125,7 @@ PfRingDevice* PfRingDeviceList::getPfRingDeviceByName(const std::string &devName return nullptr; } - return *devIter; -} - -void PfRingDeviceList::calcPfRingVersion(void* ring) -{ - pfring* ringPtr = (pfring*)ring; - uint32_t version; - if (pfring_version(ringPtr, &version) < 0) - { - PCPP_LOG_ERROR("Couldn't retrieve PF_RING version, pfring_version returned an error"); - return; - } - - char versionAsString[25]; - sprintf(versionAsString, "PF_RING v.%u.%u.%u\n", - (version & 0xFFFF0000) >> 16, - (version & 0x0000FF00) >> 8, - version & 0x000000FF); - - PCPP_LOG_DEBUG("PF_RING version is: " << versionAsString); - m_PfRingVersion = std::string(versionAsString); + return devIter->get(); } } // namespace pcpp diff --git a/Pcap++/src/WinPcapLiveDevice.cpp b/Pcap++/src/WinPcapLiveDevice.cpp index c1e0429c7e..c6a6a1121f 100644 --- a/Pcap++/src/WinPcapLiveDevice.cpp +++ b/Pcap++/src/WinPcapLiveDevice.cpp @@ -126,6 +126,11 @@ bool WinPcapLiveDevice::setMinAmountOfDataToCopyFromKernelToApplication(int size return true; } +WinPcapLiveDevice* WinPcapLiveDevice::cloneInternal(pcap_if_t& devInterface) const +{ + return new WinPcapLiveDevice(&devInterface, true, true, true); +} + } // namespace pcpp #endif // _WIN32 diff --git a/README.md b/README.md index c2e144ef47..5b06b28e93 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ [![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/seladb/PcapPlusPlus?label=Cirrus%20CI&logo=cirrusci&style=flat)](https://cirrus-ci.com/github/seladb/PcapPlusPlus) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/seladb/PcapPlusPlus/codeql.yml?branch=master&label=CodeQL&logo=github&style=flat)](https://github.com/seladb/PcapPlusPlus/actions?query=workflow%3A%22CodeQL%22) [![Codecov](https://img.shields.io/codecov/c/github/seladb/PcapPlusPlus?logo=codecov&logoColor=white)](https://app.codecov.io/github/seladb/PcapPlusPlus) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/seladb/PcapPlusPlus/badge)](https://scorecard.dev/viewer/?uri=github.com/seladb/PcapPlusPlus) [![GitHub contributors](https://img.shields.io/github/contributors/seladb/PcapPlusPlus?style=flat&label=Contributors&logo=github)](https://github.com/seladb/PcapPlusPlus/graphs/contributors) -
-[![Twitter Follow](https://img.shields.io/badge/follow-%40seladb-1DA1F2?logo=twitter&style=social)](https://twitter.com/intent/follow?screen_name=seladb) + +[![Twitter Follow](https://img.shields.io/badge/follow-%40seladb-1DA1F2?logo=x&style=social)](https://x.com/intent/follow?screen_name=seladb) [![GitHub Repo stars](https://img.shields.io/github/stars/seladb/PcapPlusPlus?style=social)]() @@ -249,20 +250,22 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the ### Application Layer (L7) -34. BGP (v4) -35. DHCP -36. DHCPv6 -37. DNS -38. FTP -39. HTTP headers (request & response) -40. NTP (v3, v4) -41. Radius -42. S7 Communication (S7comm) -43. SMTP -44. SOME/IP -45. SSH - parsing only (no editing capabilities) -46. Telnet - parsing only (no editing capabilities) -47. Generic payload +34. ASN.1 decoder and encoder +35. BGP (v4) +36. DHCP +37. DHCPv6 +38. DNS +39. FTP +40. HTTP headers (request & response) +41. LDAP +42. NTP (v3, v4) +43. Radius +44. S7 Communication (S7comm) +45. SMTP +46. SOME/IP +47. SSH - parsing only (no editing capabilities) +48. Telnet - parsing only (no editing capabilities) +49. Generic payload ## DPDK And PF_RING Support diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..17bbb18e53 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + + + +We encourage you to submit a pull request if you have a solution or fix for anything. Your contributions help advance the library and enhance safety for all users :star:. We would very much appreciate any contribution to this project. If you're interested in contributing please visit the [contribution page](https://pcapplusplus.github.io/community#contribute) in PcapPlusPlus web-site. + +## Reporting a Bug :bug: :bug: + +Simply use GitHub issues to report a bug with related information to debug the issue :pencil:. Please check the [CONTRIBUTING.md](https://github.com/seladb/PcapPlusPlus/blob/master/CONTRIBUTING.md) for details. + +## Reporting a Vulnerability :closed_lock_with_key: :eyes: + +You can use the GitHub issues to report a vulnerability publicly. But if you find a sensitive security issue and want to report privately :lock:, please email with the following information: + +- Description of the vulnerability +- Steps to reproduce the issue or a simple code piece +- Affected versions +- If applicable, a data sample (preferably `pcap/pcapng`) to reproduce +- If known, any mitigations or fixes for the issue + +If you already looked at the code and found the root cause - that's great :four_leaf_clover:! You can create a GitHub pull request as soon as possible with fix to keep safe everyone. diff --git a/Tests/ExamplesTest/requirements.txt b/Tests/ExamplesTest/requirements.txt index 264a86fa83..19ea22aef2 100644 --- a/Tests/ExamplesTest/requirements.txt +++ b/Tests/ExamplesTest/requirements.txt @@ -2,6 +2,6 @@ attrs==23.2.0 iniconfig==2.0.0 py==1.11.0 pyparsing==3.1.2 -pytest==8.1.1 +pytest==8.2.1 scapy==2.5.0 toml==0.10.2 diff --git a/Tests/Fuzzers/ReadParsedPacket.h b/Tests/Fuzzers/ReadParsedPacket.h index ca7b629264..9fc0dbb326 100644 --- a/Tests/Fuzzers/ReadParsedPacket.h +++ b/Tests/Fuzzers/ReadParsedPacket.h @@ -207,7 +207,7 @@ static void readParsedPacket(pcpp::Packet parsedPacket, pcpp::Layer* layer) if (auto tcpLayer = dynamic_cast(layer)) { auto tcpLayer2 (*tcpLayer); - tcpLayer2.addTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NOP), pcpp::PCPP_TCPOPT_NOP); + tcpLayer2.insertTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NopEolOptionEnumType::Nop), pcpp::TcpOptionEnumType::Nop); } } if (parsedPacket.isPacketOfType(pcpp::SDP)) diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index ca628f539b..0e6bbc75bc 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable( Packet++Test main.cpp + Tests/Asn1Tests.cpp Tests/BgpTests.cpp Tests/CotpTests.cpp Tests/DhcpTests.cpp @@ -17,6 +18,7 @@ add_executable( Tests/IPSecTests.cpp Tests/IPv4Tests.cpp Tests/IPv6Tests.cpp + Tests/LdapTests.cpp Tests/LLCTests.cpp Tests/NflogTests.cpp Tests/NtpTests.cpp diff --git a/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.dat b/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.dat index f68e47f79e..6f6215842d 100644 --- a/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.dat +++ b/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.dat @@ -1 +1 @@ -080027191c7830469a23fbfa08004500003d4f4c40003b064798d4c7ca090a0000060050ac80b829cb98e977158680184ed2f2aa00000101080ad3f03998000300950049454e44ae426082 \ No newline at end of file +080027191c7830469a23fbfa0800450000414f4c40003b064794d4c7ca090a0000060050ac80b829cb98e977158690184ed2dea100000101080ad3f0399800030095010303020049454e44ae426082 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.pcap b/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.pcap index bf710b8c33..2741d5e029 100644 Binary files a/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.pcap and b/Tests/Packet++Test/PacketExamples/TcpPacketWithOptions2.pcap differ diff --git a/Tests/Packet++Test/PacketExamples/ldap.pcapng b/Tests/Packet++Test/PacketExamples/ldap.pcapng new file mode 100644 index 0000000000..3637e9192a Binary files /dev/null and b/Tests/Packet++Test/PacketExamples/ldap.pcapng differ diff --git a/Tests/Packet++Test/PacketExamples/ldap_add_response.dat b/Tests/Packet++Test/PacketExamples/ldap_add_response.dat new file mode 100644 index 0000000000..97562f1d2c --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_add_response.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000420e76400023065b9e3439a258c0a85668018589f1473d0c997a93c5b5801801e3190300000101080af7118110ec708c75300c02011b69070a010004000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_bind_request1.dat b/Tests/Packet++Test/PacketExamples/ldap_bind_request1.dat new file mode 100644 index 0000000000..8c046a8464 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_bind_request1.dat @@ -0,0 +1 @@ +000c2932503f000c29a01b430800450000aaea6840004006c98cc0a80285c0a8028386110185d91123158f18ef878018013fe79900000101080a00dbc875008b457130740201026050020103043b636e3d41646d696e6973747261746f722c636e3d55736572732c64633d636c6f7564736861726b2d612c64633d6578616d706c652c64633d636f6d800e636c6f7564736861726b31323321a01d301b0419312e332e362e312e342e312e34322e322e32372e382e352e31 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_bind_request2.dat b/Tests/Packet++Test/PacketExamples/ldap_bind_request2.dat new file mode 100644 index 0000000000..29dbe8bf1d --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_bind_request2.dat @@ -0,0 +1 @@ +000c29094dfa005056c00001080045000570437740007e0645e1c0a80003ac1f01650fe20185e5eb721da044ead55018faf0ffa300003084000005420201076084000005390201030400a3840000052e040a4753532d53504e45474f0482051e6082051a06062b0601050502a082050e3082050aa024302206092a864882f71201020206092a864886f712010202060a2b06010401823702020aa28204e0048204dc608204d806092a864886f71201020201006e8204c7308204c3a003020105a10302010ea20703050020000000a38203e0618203dc308203d8a003020105a1151b1357324b332e564d4e4554312e564d2e42415345a22f302da003020102a12630241b046c6461701b1c77326b332d3130312e77326b332e766d6e6574312e766d2e62617365a382038730820383a003020117a103020107a2820375048203716a61c886ba58d162113db4268f7743a17eb476183bc0c519addea76556a3701de34903e6bd3f3fdca0b01bbccb9a8693b23fa8d1985e14922e4ca19b05a90769845a5858515bba4af2d7e59bfa8634285a2e954fb518378b8d3f2744b9bbf8842b4807879ff28e55bfba4967e8c1d3b6c4e358a561c54abbc1cb7c97b6503fe59b7fee6423dffe66fe6dcb8af00e69c53d6b576f5506990438310fb7dd1468a32fd8e0deab40b15ecfd438568370140a1edafee701a4a4b4e7b3aaefdc4b1aff5868aefe5a36294d5dd687d5a6493143d3ade8031c98d28f6c7f3dcea41435132f675f26940d1f69e573e5ece6ed5a66111ff9f4b02a8ddd19086e5b9dc0adc86a0bc1230f1b715ffc4004dfc4a7d5f78a4dc31abf830ae6e3bfd21c87fa5196549e130f6a081bafcf4170ae201c78a3829a01dba578a2ef968f2ab6668d8114dfcc65d7038f5558be7cdd9246d52247915260a40e59c48b08a1ed61427fd303917c6b34b701a4ba9a3815d4828a228cd209da137626e2029aabf6c200bf7fd63cf6d43bb618b31ac48e09613589d74a69542e909ce0dc9c57c77f7d89b966de200053a58ea58f2374513961638a30ca49ef0eec679d927e385b5da7d4d3c1a59169b4630b874a1d969e45d1fe3782089f4385024955093b308e1964d307915271aa886c3d9b64d846c88ca1341fd2f72b76679d4f258f647bc04820e42776c9ec0d01464652763a49d822c9d25b603903ebd6338952259b83a740a420d69d23aebbdf06a92d88a46ffcd8d81a47b6ec99b6cea0489cc83ef15757c4053d538446f2e6b9eba12ce4969b8d6df9b3ef574b7d401341c2f555a00f029164e5d387282c0c8791ba8c69816248e2e544a9c12b7aeba629fdeea2e111655e44b9c215924c5455eaa4ab32aea1d9cef1d86e8acf6b0ff4dcabaf4f0e2d9ae65c8bb1065e0418ff12d4626930315938bfe00a8d03e8e70e9dea9dc9ff74854cbb4dbdf700a62e77b26e50b13e2d3960c913360c84c87e801ed3df3db0e27604508cb730c5a052c068abe5826b01be9f62e33b9af8edb6667c57cb1aa879743b77a7432f75fe3ae211f96af41adef1e1c507256fe5fa2bccabe52cf8216d3410e6378506d427343458332d153a77a162c4c5f18d9f31b0c142880cad2229981720615ab26b7c13442e43178aadee436510c91bc9d5d735eb9453cf39cef5120e28603775f0483f01c3c48b5b060ca7f3a54d7c7c99a481c93081c6a003020117a281be0481bb03ab656760a3512fecc7032da8b2014659f0fb34eb76b461e4044da24d16d458e3e1c58919c74c4c0720aafb87a948152372a2483a4d1ae9b95b858a52abaa94e7aa641a8b997d7e6c6e570b5908cc549155f5e6f110c98d648978727abae3921da52a4c1fd76beb121bf3396be8f98e4acf1ebfc3b6fb7a1354c121873e59185db90030084d97864798d79eb9df30756ca1faa7a80880f74f7d93642d9ceb5e0128ced6ab096a4f015e5a032b4270231e7ff1bcd087e8b527027d \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_bind_response1.dat b/Tests/Packet++Test/PacketExamples/ldap_bind_response1.dat new file mode 100644 index 0000000000..c495197eaa --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_bind_response1.dat @@ -0,0 +1 @@ +000c29f06bd1000c29094dfa0800450000e7066740008006989eac1f0165ac1f016801850c2c7e2df27d9579fba65018faf0786100003084000000b9020200d76184000000af0a010004000400878200a4a181a130819ea0030a0100a10b06092a864882f712010202a2818904818660818306092a864886f71201020202006f743072a003020105a10302010fa2663064a003020117a25d045b4a9f10ab8996fa43f2fb4092a76cc3fa6c1f001167fac904dab067f5f2da59a7549057bd3eb46cb467fd3b01d73f5051aa632ed8d6a6e581bbab1780faabac515284139cfb44c204ae1ec25a2d58909d22ff52349e6d2e4d835b98 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_bind_response2.dat b/Tests/Packet++Test/PacketExamples/ldap_bind_response2.dat new file mode 100644 index 0000000000..a846485228 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_bind_response2.dat @@ -0,0 +1 @@ +000c29a01b43000c2932503f08004500004a396d400080063ae8c0a80283c0a80285018586118f18ef87d911238b80180103142200000101080a008b457100dbc8753084000000100201026184000000070a010004000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_compare_response.dat b/Tests/Packet++Test/PacketExamples/ldap_compare_response.dat new file mode 100644 index 0000000000..8a7c4a5937 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_compare_response.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000420e77400023065b9d3439a258c0a85668018589f1473d0ca77a93c604801801e3f15300000101080af7119431ec709f9b300c02011c6f070a010504000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_delete_response.dat b/Tests/Packet++Test/PacketExamples/ldap_delete_response.dat new file mode 100644 index 0000000000..c6aa935a98 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_delete_response.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000eb0e60400023065b0b3439a258c0a85668018589f1473d08af7a93c15b801801e5eeb200000101080af70a67c3ec6973263081b402011b6b81ae0a0120041b6f753d50656f706c652c64633d6578616d706c652c64633d636f6d04244c4441503a206572726f7220636f6465203332202d204e6f2073756368206f626a656374a36604296c6461703a2f2f6c6461702e6578616d706c652e636f6d2f64633d6578616d706c652c64633d636f6d04396c6461703a2f2f6c6461702e6578616d706c652e636f6d2f64633d6578616d706c652c64633d636f6d3f6f626a656374436c6173733f6f6e65 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_modify_dn_response.dat b/Tests/Packet++Test/PacketExamples/ldap_modify_dn_response.dat new file mode 100644 index 0000000000..8455875b80 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_modify_dn_response.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad08004500006e0e5f400023065b893439a258c0a85668018589f1473d08757a93c0e6801801e5bb3b00000101080af70a1714ec69227a303802010f6d330a0120042c6f753d6c646170332d7475746f7269616c2c64633d64656d6f312c64633d667265656970612c64633d6f72670400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_modify_response.dat b/Tests/Packet++Test/PacketExamples/ldap_modify_response.dat new file mode 100644 index 0000000000..8fe901fc10 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_modify_response.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000420e5e400023065bb63439a258c0a85668018589f1473d08677a93c05d801801e64ad200000101080af7097364ec687ecb300c02010e67070a012004000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_multiple_messages.dat b/Tests/Packet++Test/PacketExamples/ldap_multiple_messages.dat new file mode 100644 index 0000000000..85c614fc49 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_multiple_messages.dat @@ -0,0 +1 @@ +000000010006000c298dc78100000800450001673819400080062ad5c0a80abac0a80a98018594958b4216846eeb30b050182010b0e8000030840000005202010673840000004904476c6461703a2f2f466f72657374446e735a6f6e65732e6d61747269782e6c6f63616c2f44433d466f72657374446e735a6f6e65732c44433d6d61747269782c44433d6c6f63616c30840000005202010673840000004904476c6461703a2f2f446f6d61696e446e735a6f6e65732e6d61747269782e6c6f63616c2f44433d446f6d61696e446e735a6f6e65732c44433d6d61747269782c44433d6c6f63616c30840000004202010673840000003904376c6461703a2f2f6d61747269782e6c6f63616c2f434e3d436f6e66696775726174696f6e2c44433d6d61747269782c44433d6c6f63616c3084000000410201066584000000070a010004000400a0840000002b3084000000250416312e322e3834302e3131333535362e312e342e333139040b3084000000050201000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_request1.dat b/Tests/Packet++Test/PacketExamples/ldap_search_request1.dat new file mode 100644 index 0000000000..0b05660f18 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_request1.dat @@ -0,0 +1 @@ +000400010006000c29c3a87900000800450000f01fea40004006837bc0a80a98c0a80aba949501856eeb2fe88b42168450180f4a978500003081c50201066379041244433d6d61747269782c44433d6c6f63616c0a01020a0103020100020100010100a939811c322e31362e3834302e312e3131333733302e332e332e322e34362e3182106465706172746d656e744e756d62657283073e3d4e34373039301904012a04146e74736563757269747964657363726970746f72a045301f0416312e322e3834302e3131333535362e312e342e3830310405300302010730220416312e322e3834302e3131333535362e312e342e33313904083006020201f40400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat b/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat new file mode 100644 index 0000000000..a5da806765 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_request2.dat @@ -0,0 +1 @@ +08b4b11a46ad0800272cf6a20800450001181ac6400040063178c0a856683439a25889f101857a93bd5e47364582801801f5eeac00000101080aec65d6aff706caa03081e10201096381db0409636e3d736368656d610a01000a0103020100020100010100a318040b6f626a656374436c6173730409737562736368656d613081a4040d6f626a656374436c6173736573040e6174747269627574655479706573040c6c64617053796e7461786573040d6d61746368696e6752756c6573040f6d61746368696e6752756c65557365040f644954436f6e74656e7452756c6573041164495453747275637475726552756c657304096e616d65466f726d73040f63726561746554696d657374616d70040f6d6f6469667954696d657374616d7004012a04012b \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_result_done.dat b/Tests/Packet++Test/PacketExamples/ldap_search_result_done.dat new file mode 100644 index 0000000000..1e6d34f5c3 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_result_done.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000420d1f400023065cf53439a258c0a85668018589f1473645747a93bd5e801801e8688300000101080af706caa0ec65d5fc300c02010865070a010004000400 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat b/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat new file mode 100644 index 0000000000..bf5b8f6e47 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_search_result_entry.dat @@ -0,0 +1 @@ +0800272cf6a208b4b11a46ad0800450000f30e60400023065b033439a258c0a85668018589f1473d08af7a93c15b801801e5bd7f00000101080af70a67c3ec6973263081bc0201106481b60437636e3d622e736d6974682c6f753d6c646170332d7475746f7269616c2c64633d64656d6f312c64633d667265656970612c64633d6f7267307b3041040b6f626a656374636c6173733132040d696e65744f7267506572736f6e04146f7267616e697a6174696f6e616c506572736f6e0406706572736f6e0403746f70300d0402736e31070405596f756e67300f0402636e31090407622e736d69746830160409676976656e6e616d653109040742656174726978 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/ldap_unbind_request.dat b/Tests/Packet++Test/PacketExamples/ldap_unbind_request.dat new file mode 100644 index 0000000000..c6199026c5 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/ldap_unbind_request.dat @@ -0,0 +1 @@ +00000000000000000000000008004500003b921340004006aaa77f0000017f0000019e690185dcda66aedcbdd52c80182000fe2f00000101080a2fc0f4582fc0f45830050201034200 \ No newline at end of file diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index 56471aa341..b628c34f56 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -254,3 +254,11 @@ PTF_TEST_CASE(S7CommLayerCreationTest); PTF_TEST_CASE(SmtpParsingTests); PTF_TEST_CASE(SmtpCreationTests); PTF_TEST_CASE(SmtpEditTests); + +// Implemented in Asn1Tests.cpp +PTF_TEST_CASE(Asn1DecodingTest); +PTF_TEST_CASE(Asn1EncodingTest); + +// Implemented in LdapTests.cpp +PTF_TEST_CASE(LdapParsingTest); +PTF_TEST_CASE(LdapCreationTest); diff --git a/Tests/Packet++Test/Tests/Asn1Tests.cpp b/Tests/Packet++Test/Tests/Asn1Tests.cpp new file mode 100644 index 0000000000..03b8d3264c --- /dev/null +++ b/Tests/Packet++Test/Tests/Asn1Tests.cpp @@ -0,0 +1,671 @@ +#include "../TestDefinition.h" +#include "Asn1Codec.h" +#include "RawPacket.h" +#include "GeneralUtils.h" +#include +#include +#include + +PTF_TEST_CASE(Asn1DecodingTest) +{ + // Context specific + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("870b6f626a656374636c617373", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::ContextSpecific, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::NotApplicable, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 13); + PTF_ASSERT_EQUAL(record->getValueLength(), 11); + PTF_ASSERT_EQUAL(record->toString(), "ContextSpecific (7), Length: 2+11"); + auto genericRecord = record->castAs(); + auto recordValue = std::string(genericRecord->getValue(), genericRecord->getValue() + genericRecord->getValueLength()); + PTF_ASSERT_EQUAL(recordValue, "objectclass"); + } + + // Integer 1 byte + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020106", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 3); + PTF_ASSERT_EQUAL(record->getValueLength(), 1); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), 6); + PTF_ASSERT_EQUAL(record->toString(), "Integer, Length: 2+1, Value: 6"); + } + + // Integer 2 bytes + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020203e8", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 4); + PTF_ASSERT_EQUAL(record->getValueLength(), 2); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), 1000); + PTF_ASSERT_EQUAL(record->toString(), "Integer, Length: 2+2, Value: 1000"); + } + + // Integer 3 bytes + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("02030186a0", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 5); + PTF_ASSERT_EQUAL(record->getValueLength(), 3); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), 100000); + PTF_ASSERT_EQUAL(record->toString(), "Integer, Length: 2+3, Value: 100000"); + } + + // Integer 4 bytes + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020400989680", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 6); + PTF_ASSERT_EQUAL(record->getValueLength(), 4); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), 10000000); + PTF_ASSERT_EQUAL(record->toString(), "Integer, Length: 2+4, Value: 10000000"); + } + + // Integer more than 4 bytes + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020502540be400", data, 20); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, dataLen, false), std::runtime_error, "An integer ASN.1 record of more than 4 bytes is not supported"); + } + + // Enumerated + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0a022000", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Enumerated, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 4); + PTF_ASSERT_EQUAL(record->getValueLength(), 2); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), 8192); + PTF_ASSERT_EQUAL(record->toString(), "Enumerated, Length: 2+2, Value: 8192"); + } + + // Boolean - true + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0101ff", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Boolean, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 3); + PTF_ASSERT_EQUAL(record->getValueLength(), 1); + PTF_ASSERT_TRUE(record->castAs()->getValue()); + PTF_ASSERT_EQUAL(record->toString(), "Boolean, Length: 2+1, Value: true"); + } + + // Boolean - false + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("010100", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Boolean, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 3); + PTF_ASSERT_EQUAL(record->getValueLength(), 1); + PTF_ASSERT_FALSE(record->castAs()->getValue()); + PTF_ASSERT_EQUAL(record->toString(), "Boolean, Length: 2+1, Value: false"); + } + + // OctetString with printable value + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0411737562736368656d61537562656e747279", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::OctetString, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 19); + PTF_ASSERT_EQUAL(record->getValueLength(), 17); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), "subschemaSubentry"); + PTF_ASSERT_EQUAL(record->toString(), "OctetString, Length: 2+17, Value: subschemaSubentry"); + } + + // OctetString with non-printable value + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("04083006020201f40400", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::OctetString, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 10); + PTF_ASSERT_EQUAL(record->getValueLength(), 8); + PTF_ASSERT_EQUAL(record->castAs()->getValue(), "3006020201f40400"); + PTF_ASSERT_EQUAL(record->toString(), "OctetString, Length: 2+8, Value: 3006020201f40400"); + } + + // Null + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0500", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Null, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 2); + PTF_ASSERT_EQUAL(record->getValueLength(), 0); + PTF_ASSERT_NOT_NULL(record->castAs()); + PTF_ASSERT_EQUAL(record->toString(), "Null, Length: 2+0"); + } + + // Sequence + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("300a040461626364020203e8", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_TRUE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Sequence, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 12); + PTF_ASSERT_EQUAL(record->getValueLength(), 10); + + auto& subRecords = record->castAs()->getSubRecords(); + PTF_ASSERT_EQUAL(subRecords.size(), 2); + PTF_ASSERT_EQUAL(subRecords.at(0)->castAs()->getValue(), "abcd"); + PTF_ASSERT_EQUAL(subRecords.at(1)->castAs()->getValue(), 1000); + + std::ostringstream expectedString; + expectedString + << "Sequence (constructed), Length: 2+10" << std::endl + << " OctetString, Length: 2+4, Value: abcd" << std::endl + << " Integer, Length: 2+2, Value: 1000"; + + PTF_ASSERT_EQUAL(record->toString(), expectedString.str()); + } + + // Set + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("310a020203e8040461626364", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_TRUE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::Set, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 12); + PTF_ASSERT_EQUAL(record->getValueLength(), 10); + + auto& subRecords = record->castAs()->getSubRecords(); + PTF_ASSERT_EQUAL(subRecords.size(), 2); + PTF_ASSERT_EQUAL(subRecords.at(0)->castAs()->getValue(), 1000); + PTF_ASSERT_EQUAL(subRecords.at(1)->castAs()->getValue(), "abcd"); + + std::ostringstream expectedString; + expectedString + << "Set (constructed), Length: 2+10" << std::endl + << " Integer, Length: 2+2, Value: 1000" << std::endl + << " OctetString, Length: 2+4, Value: abcd"; + + PTF_ASSERT_EQUAL(record->toString(), expectedString.str()); + } + + // Application constructed + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("630a040461626364020203e8", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Application, enumclass); + PTF_ASSERT_TRUE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::NotApplicable, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 12); + PTF_ASSERT_EQUAL(record->getValueLength(), 10); + + auto& subRecords = record->castAs()->getSubRecords(); + PTF_ASSERT_EQUAL(subRecords.size(), 2); + PTF_ASSERT_EQUAL(subRecords.at(0)->castAs()->getValue(), "abcd"); + PTF_ASSERT_EQUAL(subRecords.at(1)->castAs()->getValue(), 1000); + + std::ostringstream expectedString; + expectedString + << "Application (3) (constructed), Length: 2+10" << std::endl + << " OctetString, Length: 2+4, Value: abcd" << std::endl + << " Integer, Length: 2+2, Value: 1000"; + + PTF_ASSERT_EQUAL(record->toString(), expectedString.str()); + } + + // Tag > 30 + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("1f23076d7976616c7565", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getUniversalTagType(), pcpp::Asn1UniversalTagType::ObjectIdentifierIRI, enumclass); + PTF_ASSERT_EQUAL(record->getTotalLength(), 10); + PTF_ASSERT_EQUAL(record->getValueLength(), 7); + PTF_ASSERT_EQUAL(record->toString(), "ObjectIdentifierIRI, Length: 3+7"); + auto genericRecord = record->castAs(); + auto recordValue = std::string(genericRecord->getValue(), genericRecord->getValue() + genericRecord->getValueLength()); + PTF_ASSERT_EQUAL(recordValue, "myvalue"); + } + + // Unknown tag + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("1f28076d7976616c7565", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + + PTF_ASSERT_EQUAL(record->getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record->isConstructed()); + PTF_ASSERT_EQUAL(record->getTagType(), 40); + PTF_ASSERT_EQUAL(record->getTotalLength(), 10); + PTF_ASSERT_EQUAL(record->getValueLength(), 7); + PTF_ASSERT_EQUAL(record->toString(), "Unknown, Length: 3+7"); + auto genericRecord = record->castAs(); + auto recordValue = std::string(genericRecord->getValue(), genericRecord->getValue() + genericRecord->getValueLength()); + PTF_ASSERT_EQUAL(recordValue, "myvalue"); + } + + // Tag > 127 + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("1f8100076d7976616c7565", data, 20); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, dataLen), std::invalid_argument, "ASN.1 tags with value larger than 127 are not supported"); + } + + // Not enough data to parse tag + { + uint8_t data[20]; + pcpp::hexStringToByteArray("1f8100076d7976616c7565", data, 20); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, 0), std::invalid_argument, "Cannot decode ASN.1 record tag"); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, 1), std::invalid_argument, "Cannot decode ASN.1 record tag"); + } + + // Not enough data to parse length + { + uint8_t data[20]; + pcpp::hexStringToByteArray("0500", data, 20); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, 1), std::invalid_argument, "Cannot decode ASN.1 record length"); + } + + // Incomplete record - doesn't contain the entire value + { + uint8_t data[20]; + pcpp::hexStringToByteArray("0a022000", data, 20); + PTF_ASSERT_RAISES(pcpp::Asn1Record::decode(data, 3), std::invalid_argument, "Cannot decode ASN.1 record, data doesn't contain the entire record"); + } + + // Cast as the wrong type + { + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0a022000", data, 20); + auto record = pcpp::Asn1Record::decode(data, dataLen); + #ifdef _MSC_VER + auto expectedMessage = "bad cast"; + #else + auto expectedMessage = "std::bad_cast"; + #endif + PTF_ASSERT_RAISES(record->castAs(), std::bad_cast, expectedMessage); + } +}; // Asn1DecodingTest + + +PTF_TEST_CASE(Asn1EncodingTest) +{ + // Generic record with byte array value + { + uint8_t value[] = {0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73}; + pcpp::Asn1GenericRecord record(pcpp::Asn1TagClass::ContextSpecific, false, 7, value, 11); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::ContextSpecific, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::NotApplicable, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 13); + PTF_ASSERT_EQUAL(record.getValueLength(), 11); + auto recordValue = std::string(record.getValue(), record.getValue() + record.getValueLength()); + PTF_ASSERT_EQUAL(recordValue, "objectclass"); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("870b6f626a656374636c617373", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Generic record with string value + { + pcpp::Asn1GenericRecord record(pcpp::Asn1TagClass::ContextSpecific, false, 7, "objectclass"); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::ContextSpecific, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::NotApplicable, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 13); + PTF_ASSERT_EQUAL(record.getValueLength(), 11); + auto recordValue = std::string(record.getValue(), record.getValue() + record.getValueLength()); + PTF_ASSERT_EQUAL(recordValue, "objectclass"); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("870b6f626a656374636c617373", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Record length > 128 + { + pcpp::Asn1OctetStringRecord record("12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + + uint8_t data[203]; + auto dataLen = pcpp::hexStringToByteArray("0481c83132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930", data, 203); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Record length > 256 + { + pcpp::Asn1OctetStringRecord record("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); + + uint8_t data[304]; + auto dataLen = pcpp::hexStringToByteArray("0482012c303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839", data, 304); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Integer 1 byte + { + pcpp::Asn1IntegerRecord record(6); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 3); + PTF_ASSERT_EQUAL(record.getValueLength(), 1); + PTF_ASSERT_EQUAL(record.getValue(), 6); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020106", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Integer 2 bytes + { + pcpp::Asn1IntegerRecord record(1000); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 4); + PTF_ASSERT_EQUAL(record.getValueLength(), 2); + PTF_ASSERT_EQUAL(record.getValue(), 1000); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020203e8", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Integer 3 bytes + { + pcpp::Asn1IntegerRecord record(100000); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 5); + PTF_ASSERT_EQUAL(record.getValueLength(), 3); + PTF_ASSERT_EQUAL(record.getValue(), 100000); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("02030186a0", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Integer 4 bytes + { + pcpp::Asn1IntegerRecord record(100000000); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Integer, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 6); + PTF_ASSERT_EQUAL(record.getValueLength(), 4); + PTF_ASSERT_EQUAL(record.getValue(), 100000000); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("020405f5e100", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Enumerated + { + pcpp::Asn1EnumeratedRecord record(8192); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Enumerated, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 4); + PTF_ASSERT_EQUAL(record.getValueLength(), 2); + PTF_ASSERT_EQUAL(record.getValue(), 8192); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0a022000", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // OctetString with printable value + { + pcpp::Asn1OctetStringRecord record("subschemaSubentry"); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::OctetString, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 19); + PTF_ASSERT_EQUAL(record.getValueLength(), 17); + PTF_ASSERT_EQUAL(record.getValue(), "subschemaSubentry"); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0411737562736368656d61537562656e747279", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // OctetString with non-printable value + { + constexpr size_t valueSize = 8; + uint8_t value[valueSize] = {0x30, 0x06, 0x02, 0x02, 0x01, 0xf4, 0x04, 0x00}; + pcpp::Asn1OctetStringRecord record(value, valueSize); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::OctetString, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), valueSize + 2); + PTF_ASSERT_EQUAL(record.getValueLength(), valueSize); + PTF_ASSERT_EQUAL(record.getValue(), "3006020201f40400"); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("04083006020201f40400", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Boolean - true + { + pcpp::Asn1BooleanRecord record(true); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Boolean, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 3); + PTF_ASSERT_EQUAL(record.getValueLength(), 1); + PTF_ASSERT_TRUE(record.getValue()); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0101ff", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Boolean - false + { + pcpp::Asn1BooleanRecord record(false); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("010100", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Null + { + pcpp::Asn1NullRecord record; + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_FALSE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Null, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 2); + PTF_ASSERT_EQUAL(record.getValueLength(), 0); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("0500", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen) + } + + // Sequence + { + pcpp::Asn1OctetStringRecord octestStringRecord("abcd"); + pcpp::Asn1IntegerRecord integerRecord(1000); + pcpp::Asn1SequenceRecord record({ &octestStringRecord, &integerRecord}); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_TRUE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Sequence, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 12); + PTF_ASSERT_EQUAL(record.getValueLength(), 10); + + auto& subRecords = record.getSubRecords(); + PTF_ASSERT_EQUAL(subRecords.size(), 2); + PTF_ASSERT_EQUAL(subRecords.at(0)->castAs()->getValue(), "abcd"); + PTF_ASSERT_EQUAL(subRecords.at(1)->castAs()->getValue(), 1000); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("300a040461626364020203e8", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen); + } + + // Sequence initialized with a PointerVector + { + pcpp::PointerVector subRecords; + subRecords.pushBack(new pcpp::Asn1OctetStringRecord("abcd")); + subRecords.pushBack(new pcpp::Asn1IntegerRecord(1000)); + pcpp::Asn1SequenceRecord record(subRecords); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("300a040461626364020203e8", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen); + } + + // Set + { + pcpp::Asn1OctetStringRecord octestStringRecord("abcd"); + pcpp::Asn1IntegerRecord integerRecord(1000); + pcpp::Asn1SetRecord record({ &integerRecord, &octestStringRecord }); + + PTF_ASSERT_EQUAL(record.getTagClass(), pcpp::Asn1TagClass::Universal, enumclass); + PTF_ASSERT_TRUE(record.isConstructed()); + PTF_ASSERT_EQUAL(record.getUniversalTagType(), pcpp::Asn1UniversalTagType::Set, enumclass); + PTF_ASSERT_EQUAL(record.getTotalLength(), 12); + PTF_ASSERT_EQUAL(record.getValueLength(), 10); + + auto& subRecords = record.getSubRecords(); + PTF_ASSERT_EQUAL(subRecords.size(), 2); + PTF_ASSERT_EQUAL(subRecords.at(0)->castAs()->getValue(), 1000); + PTF_ASSERT_EQUAL(subRecords.at(1)->castAs()->getValue(), "abcd"); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("310a020203e8040461626364", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen); + } + + // Set initialized with a PointerVector + { + pcpp::PointerVector subRecords; + subRecords.pushBack(new pcpp::Asn1IntegerRecord(1000)); + subRecords.pushBack(new pcpp::Asn1OctetStringRecord("abcd")); + pcpp::Asn1SetRecord record(subRecords); + + uint8_t data[20]; + auto dataLen = pcpp::hexStringToByteArray("310a020203e8040461626364", data, 20); + + auto encodedValue = record.encode(); + PTF_ASSERT_EQUAL(encodedValue.size(), dataLen); + PTF_ASSERT_BUF_COMPARE(encodedValue.data(), data, dataLen); + } +} // Asn1EncodingTest diff --git a/Tests/Packet++Test/Tests/GtpTests.cpp b/Tests/Packet++Test/Tests/GtpTests.cpp index 51b33614e9..e5cf2352d0 100644 --- a/Tests/Packet++Test/Tests/GtpTests.cpp +++ b/Tests/Packet++Test/Tests/GtpTests.cpp @@ -208,7 +208,7 @@ PTF_TEST_CASE(GtpLayerCreationTest) PTF_ASSERT_EQUAL(newExt1.getExtensionType(), 0xc0); PTF_ASSERT_EQUAL(newExt1.getTotalLength(), 4*sizeof(uint8_t)); PTF_ASSERT_EQUAL(newExt1.getContentLength(), 2*sizeof(uint8_t)); - uint16_t* content = (uint16_t*)newExt1.getContent(); + uint16_t* content = reinterpret_cast(newExt1.getContent()); PTF_ASSERT_EQUAL(be16toh(content[0]), 2308); PTF_ASSERT_TRUE(newExt1.getNextExtension().isNull()); @@ -222,7 +222,7 @@ PTF_TEST_CASE(GtpLayerCreationTest) PTF_ASSERT_EQUAL(newExt2.getExtensionType(), 0x40); PTF_ASSERT_EQUAL(newExt2.getTotalLength(), 4*sizeof(uint8_t)); PTF_ASSERT_EQUAL(newExt2.getContentLength(), 2*sizeof(uint8_t)); - content = (uint16_t*)newExt2.getContent(); + content = reinterpret_cast(newExt2.getContent()); PTF_ASSERT_EQUAL(be16toh(content[0]), 1308); PTF_ASSERT_TRUE(newExt2.getNextExtension().isNull()); @@ -276,7 +276,7 @@ PTF_TEST_CASE(GtpLayerEditTest) pcpp::GtpV1Layer::GtpExtension gtpExtension = gtpLayer->getNextExtension(); PTF_ASSERT_FALSE(gtpExtension.isNull()); - uint16_t* extContent = (uint16_t*)gtpExtension.getContent(); + uint16_t* extContent = reinterpret_cast(gtpExtension.getContent()); PTF_ASSERT_EQUAL(be16toh(extContent[0]), 1000); gtpHeader = gtpLayer->getHeader(); diff --git a/Tests/Packet++Test/Tests/LdapTests.cpp b/Tests/Packet++Test/Tests/LdapTests.cpp new file mode 100644 index 0000000000..e57f5b2aab --- /dev/null +++ b/Tests/Packet++Test/Tests/LdapTests.cpp @@ -0,0 +1,733 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include "Packet.h" +#include "SystemUtils.h" +#include "LdapLayer.h" +#include +#include + +PTF_TEST_CASE(LdapParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + // LDAP packet + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_add_response.dat"); + pcpp::Packet ldapPacket(&rawPacket1); + + auto ldapLayer = ldapPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ldapLayer); + + PTF_ASSERT_EQUAL(ldapLayer->getMessageID(), 27); + PTF_ASSERT_EQUAL(ldapLayer->getLdapOperationType(), pcpp::LdapOperationType::AddResponse, enum); + PTF_ASSERT_EQUAL(ldapLayer->getProtocol(), pcpp::LDAP); + PTF_ASSERT_EQUAL(ldapLayer->getHeaderLen(), 14); + PTF_ASSERT_TRUE(ldapLayer->getControls().empty()); + PTF_ASSERT_EQUAL(ldapLayer->toString(), "LDAP Layer, AddResponse, Success"); + + pcpp::Asn1IntegerRecord messageIdRecord(27); + + pcpp::Asn1EnumeratedRecord enumeratedRecord(0); + pcpp::Asn1OctetStringRecord stringRecord1(""); + pcpp::Asn1OctetStringRecord stringRecord2(""); + pcpp::Asn1ConstructedRecord expectedOperationRecord(pcpp::Asn1TagClass::Application, 9, {&enumeratedRecord, &stringRecord1, &stringRecord2}); + + pcpp::Asn1SequenceRecord expectedRootRecord({&messageIdRecord, &expectedOperationRecord}); + + PTF_ASSERT_EQUAL(ldapLayer->getRootAsn1Record()->toString(), expectedRootRecord.toString()); + PTF_ASSERT_EQUAL(ldapLayer->getLdapOperationAsn1Record()->toString(), expectedOperationRecord.toString()); + } + + // LDAP with multiple controls + { + READ_FILE_AND_CREATE_PACKET_LINKTYPE(1, "PacketExamples/ldap_search_request1.dat", pcpp::LINKTYPE_LINUX_SLL); + pcpp::Packet ldapWithControlsPacket(&rawPacket1); + + auto ldapLayer = ldapWithControlsPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ldapLayer); + + auto controls = ldapLayer->getControls(); + std::vector expectedControls = { + {"1.2.840.113556.1.4.801", "3003020107"}, + {"1.2.840.113556.1.4.319", "3006020201f40400"} + }; + PTF_ASSERT_VECTORS_EQUAL(controls, expectedControls); + } + + // LDAP with partial controls + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request1.dat"); + pcpp::Packet ldapWithControlsPacket(&rawPacket1); + + auto ldapLayer = ldapWithControlsPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ldapLayer); + + auto controls = ldapLayer->getControls(); + std::vector expectedControls = {{"1.3.6.1.4.1.42.2.27.8.5.1"}}; + PTF_ASSERT_VECTORS_EQUAL(controls, expectedControls); + } + + // LdapLayer tryGet + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request1.dat"); + buffer1[68] = 0x04; + pcpp::Packet malformedLdapPacket(&rawPacket1); + + auto malformedLdapLayer = malformedLdapPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(malformedLdapLayer); + + uint16_t messageId; + PTF_ASSERT_FALSE(malformedLdapLayer->tryGet(&pcpp::LdapLayer::getMessageID, messageId)); + + std::vector controls; + PTF_ASSERT_TRUE(malformedLdapLayer->tryGet(&pcpp::LdapLayer::getControls, controls)); + std::vector expectedControls = {{"1.3.6.1.4.1.42.2.27.8.5.1"}}; + PTF_ASSERT_VECTORS_EQUAL(controls, expectedControls); + } + + // Multiple LDAP messages in the same packet + { + READ_FILE_AND_CREATE_PACKET_LINKTYPE(1, "PacketExamples/ldap_multiple_messages.dat", pcpp::LINKTYPE_LINUX_SLL); + pcpp::Packet multipleLdapPacket(&rawPacket1); + + auto ldapLayer = multipleLdapPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ldapLayer); + + for (int i = 0; i < 3; i++) + { + PTF_ASSERT_EQUAL(ldapLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultReference, enum); + ldapLayer = dynamic_cast(ldapLayer->getNextLayer()); + PTF_ASSERT_NOT_NULL(ldapLayer); + } + + PTF_ASSERT_EQUAL(ldapLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultDone, enum); + PTF_ASSERT_NULL(ldapLayer->getNextLayer()); + } + + // BindRequest with simple authentication + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request1.dat"); + pcpp::Packet bindRequestPacket(&rawPacket1); + + auto bindRequestLayer = bindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(bindRequestLayer); + PTF_ASSERT_EQUAL(bindRequestLayer->getMessageID(), 2); + PTF_ASSERT_EQUAL(bindRequestLayer->getLdapOperationType(), pcpp::LdapOperationType::BindRequest, enum); + PTF_ASSERT_EQUAL(bindRequestLayer->getVersion(), 3); + PTF_ASSERT_EQUAL(bindRequestLayer->getName(), "cn=Administrator,cn=Users,dc=cloudshark-a,dc=example,dc=com"); + PTF_ASSERT_EQUAL(bindRequestLayer->getAuthenticationType(), pcpp::LdapBindRequestLayer::AuthenticationType::Simple, enumclass); + PTF_ASSERT_EQUAL(bindRequestLayer->getSimpleAuthentication(), "cloudshark123!"); + PTF_ASSERT_RAISES(bindRequestLayer->getSaslAuthentication(), std::invalid_argument, "Authentication type is not sasl"); + pcpp::LdapBindRequestLayer::SaslAuthentication saslAuthentication; + PTF_ASSERT_FALSE(bindRequestLayer->tryGet(&pcpp::LdapBindRequestLayer::getSaslAuthentication, saslAuthentication)); + PTF_ASSERT_EQUAL(bindRequestLayer->toString(), "LDAP Layer, BindRequest, simple"); + } + + // BindRequest with sasl authentication + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request2.dat"); + pcpp::Packet bindRequestPacket(&rawPacket1); + + auto bindRequestLayer = bindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(bindRequestLayer); + PTF_ASSERT_EQUAL(bindRequestLayer->getMessageID(), 7); + PTF_ASSERT_EQUAL(bindRequestLayer->getLdapOperationType(), pcpp::LdapOperationType::BindRequest, enum); + PTF_ASSERT_EQUAL(bindRequestLayer->getVersion(), 3); + PTF_ASSERT_EQUAL(bindRequestLayer->getName(), ""); + PTF_ASSERT_EQUAL(bindRequestLayer->getAuthenticationType(), pcpp::LdapBindRequestLayer::AuthenticationType::Sasl, enumclass); + pcpp::LdapBindRequestLayer::SaslAuthentication expectedSaslAuthentication { + "GSS-SPNEGO", + {0x60, 0x82, 0x05, 0x1a, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x82, 0x05, 0x0e, 0x30, 0x82, 0x05, 0x0a, 0xa0, 0x24, 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x04, 0xe0, 0x04, 0x82, 0x04, 0xdc, 0x60, 0x82, 0x04, 0xd8, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x01, 0x00, 0x6e, 0x82, 0x04, 0xc7, 0x30, 0x82, 0x04, 0xc3, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x03, 0x02, 0x01, 0x0e, 0xa2, 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xa3, 0x82, 0x03, 0xe0, + 0x61, 0x82, 0x03, 0xdc, 0x30, 0x82, 0x03, 0xd8, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x15, 0x1b, 0x13, 0x57, 0x32, 0x4b, 0x33, 0x2e, 0x56, 0x4d, 0x4e, 0x45, 0x54, 0x31, 0x2e, 0x56, 0x4d, 0x2e, 0x42, 0x41, 0x53, 0x45, 0xa2, 0x2f, + 0x30, 0x2d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0xa1, 0x26, 0x30, 0x24, 0x1b, 0x04, 0x6c, 0x64, 0x61, 0x70, 0x1b, 0x1c, 0x77, 0x32, 0x6b, 0x33, 0x2d, 0x31, 0x30, 0x31, 0x2e, 0x77, 0x32, 0x6b, 0x33, 0x2e, 0x76, 0x6d, 0x6e, 0x65, 0x74, + 0x31, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65, 0xa3, 0x82, 0x03, 0x87, 0x30, 0x82, 0x03, 0x83, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa1, 0x03, 0x02, 0x01, 0x07, 0xa2, 0x82, 0x03, 0x75, 0x04, 0x82, 0x03, 0x71, 0x6a, 0x61, 0xc8, + 0x86, 0xba, 0x58, 0xd1, 0x62, 0x11, 0x3d, 0xb4, 0x26, 0x8f, 0x77, 0x43, 0xa1, 0x7e, 0xb4, 0x76, 0x18, 0x3b, 0xc0, 0xc5, 0x19, 0xad, 0xde, 0xa7, 0x65, 0x56, 0xa3, 0x70, 0x1d, 0xe3, 0x49, 0x03, 0xe6, 0xbd, 0x3f, 0x3f, 0xdc, 0xa0, + 0xb0, 0x1b, 0xbc, 0xcb, 0x9a, 0x86, 0x93, 0xb2, 0x3f, 0xa8, 0xd1, 0x98, 0x5e, 0x14, 0x92, 0x2e, 0x4c, 0xa1, 0x9b, 0x05, 0xa9, 0x07, 0x69, 0x84, 0x5a, 0x58, 0x58, 0x51, 0x5b, 0xba, 0x4a, 0xf2, 0xd7, 0xe5, 0x9b, 0xfa, 0x86, 0x34, + 0x28, 0x5a, 0x2e, 0x95, 0x4f, 0xb5, 0x18, 0x37, 0x8b, 0x8d, 0x3f, 0x27, 0x44, 0xb9, 0xbb, 0xf8, 0x84, 0x2b, 0x48, 0x07, 0x87, 0x9f, 0xf2, 0x8e, 0x55, 0xbf, 0xba, 0x49, 0x67, 0xe8, 0xc1, 0xd3, 0xb6, 0xc4, 0xe3, 0x58, 0xa5, 0x61, + 0xc5, 0x4a, 0xbb, 0xc1, 0xcb, 0x7c, 0x97, 0xb6, 0x50, 0x3f, 0xe5, 0x9b, 0x7f, 0xee, 0x64, 0x23, 0xdf, 0xfe, 0x66, 0xfe, 0x6d, 0xcb, 0x8a, 0xf0, 0x0e, 0x69, 0xc5, 0x3d, 0x6b, 0x57, 0x6f, 0x55, 0x06, 0x99, 0x04, 0x38, 0x31, 0x0f, + 0xb7, 0xdd, 0x14, 0x68, 0xa3, 0x2f, 0xd8, 0xe0, 0xde, 0xab, 0x40, 0xb1, 0x5e, 0xcf, 0xd4, 0x38, 0x56, 0x83, 0x70, 0x14, 0x0a, 0x1e, 0xda, 0xfe, 0xe7, 0x01, 0xa4, 0xa4, 0xb4, 0xe7, 0xb3, 0xaa, 0xef, 0xdc, 0x4b, 0x1a, 0xff, 0x58, + 0x68, 0xae, 0xfe, 0x5a, 0x36, 0x29, 0x4d, 0x5d, 0xd6, 0x87, 0xd5, 0xa6, 0x49, 0x31, 0x43, 0xd3, 0xad, 0xe8, 0x03, 0x1c, 0x98, 0xd2, 0x8f, 0x6c, 0x7f, 0x3d, 0xce, 0xa4, 0x14, 0x35, 0x13, 0x2f, 0x67, 0x5f, 0x26, 0x94, 0x0d, 0x1f, + 0x69, 0xe5, 0x73, 0xe5, 0xec, 0xe6, 0xed, 0x5a, 0x66, 0x11, 0x1f, 0xf9, 0xf4, 0xb0, 0x2a, 0x8d, 0xdd, 0x19, 0x08, 0x6e, 0x5b, 0x9d, 0xc0, 0xad, 0xc8, 0x6a, 0x0b, 0xc1, 0x23, 0x0f, 0x1b, 0x71, 0x5f, 0xfc, 0x40, 0x04, 0xdf, 0xc4, + 0xa7, 0xd5, 0xf7, 0x8a, 0x4d, 0xc3, 0x1a, 0xbf, 0x83, 0x0a, 0xe6, 0xe3, 0xbf, 0xd2, 0x1c, 0x87, 0xfa, 0x51, 0x96, 0x54, 0x9e, 0x13, 0x0f, 0x6a, 0x08, 0x1b, 0xaf, 0xcf, 0x41, 0x70, 0xae, 0x20, 0x1c, 0x78, 0xa3, 0x82, 0x9a, 0x01, + 0xdb, 0xa5, 0x78, 0xa2, 0xef, 0x96, 0x8f, 0x2a, 0xb6, 0x66, 0x8d, 0x81, 0x14, 0xdf, 0xcc, 0x65, 0xd7, 0x03, 0x8f, 0x55, 0x58, 0xbe, 0x7c, 0xdd, 0x92, 0x46, 0xd5, 0x22, 0x47, 0x91, 0x52, 0x60, 0xa4, 0x0e, 0x59, 0xc4, 0x8b, 0x08, + 0xa1, 0xed, 0x61, 0x42, 0x7f, 0xd3, 0x03, 0x91, 0x7c, 0x6b, 0x34, 0xb7, 0x01, 0xa4, 0xba, 0x9a, 0x38, 0x15, 0xd4, 0x82, 0x8a, 0x22, 0x8c, 0xd2, 0x09, 0xda, 0x13, 0x76, 0x26, 0xe2, 0x02, 0x9a, 0xab, 0xf6, 0xc2, 0x00, 0xbf, 0x7f, + 0xd6, 0x3c, 0xf6, 0xd4, 0x3b, 0xb6, 0x18, 0xb3, 0x1a, 0xc4, 0x8e, 0x09, 0x61, 0x35, 0x89, 0xd7, 0x4a, 0x69, 0x54, 0x2e, 0x90, 0x9c, 0xe0, 0xdc, 0x9c, 0x57, 0xc7, 0x7f, 0x7d, 0x89, 0xb9, 0x66, 0xde, 0x20, 0x00, 0x53, 0xa5, 0x8e, + 0xa5, 0x8f, 0x23, 0x74, 0x51, 0x39, 0x61, 0x63, 0x8a, 0x30, 0xca, 0x49, 0xef, 0x0e, 0xec, 0x67, 0x9d, 0x92, 0x7e, 0x38, 0x5b, 0x5d, 0xa7, 0xd4, 0xd3, 0xc1, 0xa5, 0x91, 0x69, 0xb4, 0x63, 0x0b, 0x87, 0x4a, 0x1d, 0x96, 0x9e, 0x45, + 0xd1, 0xfe, 0x37, 0x82, 0x08, 0x9f, 0x43, 0x85, 0x02, 0x49, 0x55, 0x09, 0x3b, 0x30, 0x8e, 0x19, 0x64, 0xd3, 0x07, 0x91, 0x52, 0x71, 0xaa, 0x88, 0x6c, 0x3d, 0x9b, 0x64, 0xd8, 0x46, 0xc8, 0x8c, 0xa1, 0x34, 0x1f, 0xd2, 0xf7, 0x2b, + 0x76, 0x67, 0x9d, 0x4f, 0x25, 0x8f, 0x64, 0x7b, 0xc0, 0x48, 0x20, 0xe4, 0x27, 0x76, 0xc9, 0xec, 0x0d, 0x01, 0x46, 0x46, 0x52, 0x76, 0x3a, 0x49, 0xd8, 0x22, 0xc9, 0xd2, 0x5b, 0x60, 0x39, 0x03, 0xeb, 0xd6, 0x33, 0x89, 0x52, 0x25, + 0x9b, 0x83, 0xa7, 0x40, 0xa4, 0x20, 0xd6, 0x9d, 0x23, 0xae, 0xbb, 0xdf, 0x06, 0xa9, 0x2d, 0x88, 0xa4, 0x6f, 0xfc, 0xd8, 0xd8, 0x1a, 0x47, 0xb6, 0xec, 0x99, 0xb6, 0xce, 0xa0, 0x48, 0x9c, 0xc8, 0x3e, 0xf1, 0x57, 0x57, 0xc4, 0x05, + 0x3d, 0x53, 0x84, 0x46, 0xf2, 0xe6, 0xb9, 0xeb, 0xa1, 0x2c, 0xe4, 0x96, 0x9b, 0x8d, 0x6d, 0xf9, 0xb3, 0xef, 0x57, 0x4b, 0x7d, 0x40, 0x13, 0x41, 0xc2, 0xf5, 0x55, 0xa0, 0x0f, 0x02, 0x91, 0x64, 0xe5, 0xd3, 0x87, 0x28, 0x2c, 0x0c, + 0x87, 0x91, 0xba, 0x8c, 0x69, 0x81, 0x62, 0x48, 0xe2, 0xe5, 0x44, 0xa9, 0xc1, 0x2b, 0x7a, 0xeb, 0xa6, 0x29, 0xfd, 0xee, 0xa2, 0xe1, 0x11, 0x65, 0x5e, 0x44, 0xb9, 0xc2, 0x15, 0x92, 0x4c, 0x54, 0x55, 0xea, 0xa4, 0xab, 0x32, 0xae, + 0xa1, 0xd9, 0xce, 0xf1, 0xd8, 0x6e, 0x8a, 0xcf, 0x6b, 0x0f, 0xf4, 0xdc, 0xab, 0xaf, 0x4f, 0x0e, 0x2d, 0x9a, 0xe6, 0x5c, 0x8b, 0xb1, 0x06, 0x5e, 0x04, 0x18, 0xff, 0x12, 0xd4, 0x62, 0x69, 0x30, 0x31, 0x59, 0x38, 0xbf, 0xe0, 0x0a, + 0x8d, 0x03, 0xe8, 0xe7, 0x0e, 0x9d, 0xea, 0x9d, 0xc9, 0xff, 0x74, 0x85, 0x4c, 0xbb, 0x4d, 0xbd, 0xf7, 0x00, 0xa6, 0x2e, 0x77, 0xb2, 0x6e, 0x50, 0xb1, 0x3e, 0x2d, 0x39, 0x60, 0xc9, 0x13, 0x36, 0x0c, 0x84, 0xc8, 0x7e, 0x80, 0x1e, + 0xd3, 0xdf, 0x3d, 0xb0, 0xe2, 0x76, 0x04, 0x50, 0x8c, 0xb7, 0x30, 0xc5, 0xa0, 0x52, 0xc0, 0x68, 0xab, 0xe5, 0x82, 0x6b, 0x01, 0xbe, 0x9f, 0x62, 0xe3, 0x3b, 0x9a, 0xf8, 0xed, 0xb6, 0x66, 0x7c, 0x57, 0xcb, 0x1a, 0xa8, 0x79, 0x74, + 0x3b, 0x77, 0xa7, 0x43, 0x2f, 0x75, 0xfe, 0x3a, 0xe2, 0x11, 0xf9, 0x6a, 0xf4, 0x1a, 0xde, 0xf1, 0xe1, 0xc5, 0x07, 0x25, 0x6f, 0xe5, 0xfa, 0x2b, 0xcc, 0xab, 0xe5, 0x2c, 0xf8, 0x21, 0x6d, 0x34, 0x10, 0xe6, 0x37, 0x85, 0x06, 0xd4, + 0x27, 0x34, 0x34, 0x58, 0x33, 0x2d, 0x15, 0x3a, 0x77, 0xa1, 0x62, 0xc4, 0xc5, 0xf1, 0x8d, 0x9f, 0x31, 0xb0, 0xc1, 0x42, 0x88, 0x0c, 0xad, 0x22, 0x29, 0x98, 0x17, 0x20, 0x61, 0x5a, 0xb2, 0x6b, 0x7c, 0x13, 0x44, 0x2e, 0x43, 0x17, + 0x8a, 0xad, 0xee, 0x43, 0x65, 0x10, 0xc9, 0x1b, 0xc9, 0xd5, 0xd7, 0x35, 0xeb, 0x94, 0x53, 0xcf, 0x39, 0xce, 0xf5, 0x12, 0x0e, 0x28, 0x60, 0x37, 0x75, 0xf0, 0x48, 0x3f, 0x01, 0xc3, 0xc4, 0x8b, 0x5b, 0x06, 0x0c, 0xa7, 0xf3, 0xa5, + 0x4d, 0x7c, 0x7c, 0x99, 0xa4, 0x81, 0xc9, 0x30, 0x81, 0xc6, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa2, 0x81, 0xbe, 0x04, 0x81, 0xbb, 0x03, 0xab, 0x65, 0x67, 0x60, 0xa3, 0x51, 0x2f, 0xec, 0xc7, 0x03, 0x2d, 0xa8, 0xb2, 0x01, 0x46, 0x59, + 0xf0, 0xfb, 0x34, 0xeb, 0x76, 0xb4, 0x61, 0xe4, 0x04, 0x4d, 0xa2, 0x4d, 0x16, 0xd4, 0x58, 0xe3, 0xe1, 0xc5, 0x89, 0x19, 0xc7, 0x4c, 0x4c, 0x07, 0x20, 0xaa, 0xfb, 0x87, 0xa9, 0x48, 0x15, 0x23, 0x72, 0xa2, 0x48, 0x3a, 0x4d, 0x1a, + 0xe9, 0xb9, 0x5b, 0x85, 0x8a, 0x52, 0xab, 0xaa, 0x94, 0xe7, 0xaa, 0x64, 0x1a, 0x8b, 0x99, 0x7d, 0x7e, 0x6c, 0x6e, 0x57, 0x0b, 0x59, 0x08, 0xcc, 0x54, 0x91, 0x55, 0xf5, 0xe6, 0xf1, 0x10, 0xc9, 0x8d, 0x64, 0x89, 0x78, 0x72, 0x7a, + 0xba, 0xe3, 0x92, 0x1d, 0xa5, 0x2a, 0x4c, 0x1f, 0xd7, 0x6b, 0xeb, 0x12, 0x1b, 0xf3, 0x39, 0x6b, 0xe8, 0xf9, 0x8e, 0x4a, 0xcf, 0x1e, 0xbf, 0xc3, 0xb6, 0xfb, 0x7a, 0x13, 0x54, 0xc1, 0x21, 0x87, 0x3e, 0x59, 0x18, 0x5d, 0xb9, 0x00, + 0x30, 0x08, 0x4d, 0x97, 0x86, 0x47, 0x98, 0xd7, 0x9e, 0xb9, 0xdf, 0x30, 0x75, 0x6c, 0xa1, 0xfa, 0xa7, 0xa8, 0x08, 0x80, 0xf7, 0x4f, 0x7d, 0x93, 0x64, 0x2d, 0x9c, 0xeb, 0x5e, 0x01, 0x28, 0xce, 0xd6, 0xab, 0x09, 0x6a, 0x4f, 0x01, + 0x5e, 0x5a, 0x03, 0x2b, 0x42, 0x70, 0x23, 0x1e, 0x7f, 0xf1, 0xbc, 0xd0, 0x87, 0xe8, 0xb5, 0x27, 0x02, 0x7d} + }; + PTF_ASSERT_EQUAL(bindRequestLayer->getSaslAuthentication(), expectedSaslAuthentication); + PTF_ASSERT_RAISES(bindRequestLayer->getSimpleAuthentication(), std::invalid_argument, "Authentication type is not simple"); + std::string simpleAuthentication; + PTF_ASSERT_FALSE(bindRequestLayer->tryGet(&pcpp::LdapBindRequestLayer::getSimpleAuthentication, simpleAuthentication)); + PTF_ASSERT_EQUAL(bindRequestLayer->toString(), "LDAP Layer, BindRequest, sasl"); + } + + // BindResponse with server sals credentials + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_response1.dat"); + pcpp::Packet bindResponsePacket(&rawPacket1); + + auto bindResponseLayer = bindResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(bindResponseLayer); + PTF_ASSERT_EQUAL(bindResponseLayer->getMessageID(), 215); + PTF_ASSERT_EQUAL(bindResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::BindResponse, enum); + PTF_ASSERT_EQUAL(bindResponseLayer->getResultCode(), pcpp::LdapResultCode::Success, enum); + PTF_ASSERT_EQUAL(bindResponseLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(bindResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(bindResponseLayer->getReferral(), std::vector()); + std::vector expectedServerSaslCredentials = { + 0xa1, 0x81, 0xa1, 0x30, 0x81, 0x9e, 0xa0, 0x03, 0x0a, 0x01, 0x00, 0xa1, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, 0xa2, 0x81, 0x89, 0x04, 0x81, 0x86, 0x60, 0x81, 0x83, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x02, 0x00, 0x6f, 0x74, 0x30, 0x72, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x03, 0x02, 0x01, 0x0f, 0xa2, 0x66, 0x30, 0x64, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa2, 0x5d, 0x04, 0x5b, 0x4a, 0x9f, 0x10, + 0xab, 0x89, 0x96, 0xfa, 0x43, 0xf2, 0xfb, 0x40, 0x92, 0xa7, 0x6c, 0xc3, 0xfa, 0x6c, 0x1f, 0x00, 0x11, 0x67, 0xfa, 0xc9, 0x04, 0xda, 0xb0, 0x67, 0xf5, 0xf2, 0xda, 0x59, 0xa7, 0x54, 0x90, 0x57, 0xbd, 0x3e, 0xb4, 0x6c, 0xb4, 0x67, + 0xfd, 0x3b, 0x01, 0xd7, 0x3f, 0x50, 0x51, 0xaa, 0x63, 0x2e, 0xd8, 0xd6, 0xa6, 0xe5, 0x81, 0xbb, 0xab, 0x17, 0x80, 0xfa, 0xab, 0xac, 0x51, 0x52, 0x84, 0x13, 0x9c, 0xfb, 0x44, 0xc2, 0x04, 0xae, 0x1e, 0xc2, 0x5a, 0x2d, 0x58, 0x90, + 0x9d, 0x22, 0xff, 0x52, 0x34, 0x9e, 0x6d, 0x2e, 0x4d, 0x83, 0x5b, 0x98}; + PTF_ASSERT_VECTORS_EQUAL(bindResponseLayer->getServerSaslCredentials(), expectedServerSaslCredentials); + } + + // BindResponse without server sals credentials + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_response2.dat"); + pcpp::Packet bindResponsePacket(&rawPacket1); + + auto bindResponseLayer = bindResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(bindResponseLayer); + PTF_ASSERT_EQUAL(bindResponseLayer->getMessageID(), 2); + PTF_ASSERT_EQUAL(bindResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::BindResponse, enum); + PTF_ASSERT_EQUAL(bindResponseLayer->getResultCode(), pcpp::LdapResultCode::Success, enum); + PTF_ASSERT_EQUAL(bindResponseLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(bindResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(bindResponseLayer->getReferral(), std::vector()); + PTF_ASSERT_VECTORS_EQUAL(bindResponseLayer->getServerSaslCredentials(), std::vector()); + } + + // UnbindRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_unbind_request.dat"); + pcpp::Packet unbindRequestPacket(&rawPacket1); + + auto unbindRequestLayer = unbindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(unbindRequestLayer); + PTF_ASSERT_EQUAL(unbindRequestLayer->getMessageID(), 3); + PTF_ASSERT_EQUAL(unbindRequestLayer->getLdapOperationType(), pcpp::LdapOperationType::UnbindRequest, enum); + PTF_ASSERT_EQUAL(unbindRequestLayer->toString(), "LDAP Layer, UnbindRequest"); + } + + // SearchRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_request2.dat"); + pcpp::Packet searchRequestPacket(&rawPacket1); + + auto searchRequestLayer = searchRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(searchRequestLayer); + PTF_ASSERT_EQUAL(searchRequestLayer->getMessageID(), 9); + PTF_ASSERT_EQUAL(searchRequestLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchRequest, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getBaseObject(), "cn=schema"); + PTF_ASSERT_EQUAL(searchRequestLayer->getScope(), pcpp::LdapSearchRequestLayer::SearchRequestScope::BaseObject, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getDerefAlias(), pcpp::LdapSearchRequestLayer::DerefAliases::DerefAlways, enum); + PTF_ASSERT_EQUAL(searchRequestLayer->getSizeLimit(), 0); + PTF_ASSERT_EQUAL(searchRequestLayer->getTimeLimit(), 0); + PTF_ASSERT_FALSE(searchRequestLayer->getTypesOnly()); + std::ostringstream expectedFilter; + expectedFilter + << "ContextSpecific (3) (constructed), Length: 2+24" << std::endl + << " OctetString, Length: 2+11, Value: objectClass" << std::endl + << " OctetString, Length: 2+9, Value: subschema"; + PTF_ASSERT_EQUAL(searchRequestLayer->getFilter()->toString(), expectedFilter.str()); + PTF_ASSERT_EQUAL(searchRequestLayer->toString(), "LDAP Layer, SearchRequest, \"cn=schema\", BaseObject"); + auto attributes = searchRequestLayer->getAttributes(); + std::vector expectedAttributes = { + "objectClasses", + "attributeTypes", + "ldapSyntaxes", + "matchingRules", + "matchingRuleUse", + "dITContentRules", + "dITStructureRules", + "nameForms", + "createTimestamp", + "modifyTimestamp", + "*", + "+" + }; + PTF_ASSERT_VECTORS_EQUAL(attributes, expectedAttributes); + } + + // SearchResultEntry + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_entry.dat"); + pcpp::Packet searchResEntryPacket(&rawPacket1); + + auto searchResultEntryLayer = searchResEntryPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(searchResultEntryLayer); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getMessageID(), 16); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultEntry, enum); + PTF_ASSERT_EQUAL(searchResultEntryLayer->getObjectName(), "cn=b.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org"); + std::vector expectedAttributes = { + {"objectclass", {"inetOrgPerson", "organizationalPerson", "person", "top"}}, + {"sn", {"Young"}}, + {"cn", {"b.smith"}}, + {"givenname", {"Beatrix"}} + }; + PTF_ASSERT_VECTORS_EQUAL(searchResultEntryLayer->getAttributes(), expectedAttributes); + } + + // LdapSearchResultDoneLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_done.dat"); + pcpp::Packet searchResultDonePacket(&rawPacket1); + + auto searchResultDoneLayer = searchResultDonePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(searchResultDoneLayer); + PTF_ASSERT_EQUAL(searchResultDoneLayer->getMessageID(), 8); + PTF_ASSERT_EQUAL(searchResultDoneLayer->getLdapOperationType(), pcpp::LdapOperationType::SearchResultDone, enum); + PTF_ASSERT_EQUAL(searchResultDoneLayer->getResultCode(), pcpp::LdapResultCode::Success, enum); + PTF_ASSERT_EQUAL(searchResultDoneLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(searchResultDoneLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(searchResultDoneLayer->getReferral(), std::vector()); + PTF_ASSERT_EQUAL(searchResultDoneLayer->toString(), "LDAP Layer, SearchResultDone, Success"); + } + + // LdapModifyResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_modify_response.dat"); + pcpp::Packet modifyResponsePacket(&rawPacket1); + + auto modifyResponseLayer = modifyResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(modifyResponseLayer); + PTF_ASSERT_EQUAL(modifyResponseLayer->getMessageID(), 14); + PTF_ASSERT_EQUAL(modifyResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::ModifyResponse, enum); + PTF_ASSERT_EQUAL(modifyResponseLayer->getResultCode(), pcpp::LdapResultCode::NoSuchObject, enum); + PTF_ASSERT_EQUAL(modifyResponseLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(modifyResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(modifyResponseLayer->getReferral(), std::vector()); + PTF_ASSERT_EQUAL(modifyResponseLayer->toString(), "LDAP Layer, ModifyResponse, NoSuchObject"); + } + + // LdapAddResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_add_response.dat"); + pcpp::Packet addResponsePacket(&rawPacket1); + + auto addResponseLayer = addResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(addResponseLayer); + PTF_ASSERT_EQUAL(addResponseLayer->getMessageID(), 27); + PTF_ASSERT_EQUAL(addResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::AddResponse, enum); + PTF_ASSERT_EQUAL(addResponseLayer->getResultCode(), pcpp::LdapResultCode::Success, enum); + PTF_ASSERT_EQUAL(addResponseLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(addResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(addResponseLayer->getReferral(), std::vector()); + PTF_ASSERT_EQUAL(addResponseLayer->toString(), "LDAP Layer, AddResponse, Success"); + } + + // LdapDeleteResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_delete_response.dat"); + pcpp::Packet deleteResponsePacket(&rawPacket1); + + auto deleteResponseLayer = deleteResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(deleteResponseLayer); + PTF_ASSERT_EQUAL(deleteResponseLayer->getMessageID(), 27); + PTF_ASSERT_EQUAL(deleteResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::DeleteResponse, enum); + PTF_ASSERT_EQUAL(deleteResponseLayer->getResultCode(), pcpp::LdapResultCode::NoSuchObject, enum); + PTF_ASSERT_EQUAL(deleteResponseLayer->getMatchedDN(), "ou=People,dc=example,dc=com"); + PTF_ASSERT_EQUAL(deleteResponseLayer->getDiagnosticMessage(), "LDAP: error code 32 - No such object"); + + std::vector expectedReferral = { + "ldap://ldap.example.com/dc=example,dc=com", + "ldap://ldap.example.com/dc=example,dc=com?objectClass?one" + }; + PTF_ASSERT_VECTORS_EQUAL(deleteResponseLayer->getReferral(), expectedReferral); + PTF_ASSERT_EQUAL(deleteResponseLayer->toString(), "LDAP Layer, DeleteResponse, NoSuchObject"); + } + + // LdapModifyDNResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_modify_dn_response.dat"); + pcpp::Packet modifyDNResponsePacket(&rawPacket1); + + auto modifyDNResponseLayer = modifyDNResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(modifyDNResponseLayer); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->getMessageID(), 15); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::ModifyDNResponse, enum); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->getResultCode(), pcpp::LdapResultCode::NoSuchObject, enum); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->getMatchedDN(), "ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org"); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(modifyDNResponseLayer->getReferral(), std::vector()); + PTF_ASSERT_EQUAL(modifyDNResponseLayer->toString(), "LDAP Layer, ModifyDNResponse, NoSuchObject"); + } + + // LdapCompareResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_compare_response.dat"); + pcpp::Packet compareResponsePacket(&rawPacket1); + + auto compareResponseLayer = compareResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(compareResponseLayer); + PTF_ASSERT_EQUAL(compareResponseLayer->getMessageID(), 28); + PTF_ASSERT_EQUAL(compareResponseLayer->getLdapOperationType(), pcpp::LdapOperationType::CompareResponse, enum); + PTF_ASSERT_EQUAL(compareResponseLayer->getResultCode(), pcpp::LdapResultCode::CompareFalse, enum); + PTF_ASSERT_EQUAL(compareResponseLayer->getMatchedDN(), ""); + PTF_ASSERT_EQUAL(compareResponseLayer->getDiagnosticMessage(), ""); + PTF_ASSERT_VECTORS_EQUAL(compareResponseLayer->getReferral(), std::vector()); + PTF_ASSERT_EQUAL(compareResponseLayer->toString(), "LDAP Layer, CompareResponse, CompareFalse"); + } +} // LdapParsingTest + + +PTF_TEST_CASE(LdapCreationTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + // LDAP packet with multiple controls + { + READ_FILE_AND_CREATE_PACKET_LINKTYPE(1, "PacketExamples/ldap_search_request1.dat", pcpp::LINKTYPE_LINUX_SLL); + pcpp::Packet ldapPacket(&rawPacket1); + + pcpp::Asn1OctetStringRecord stringRecord("DC=matrix,DC=local"); + pcpp::Asn1EnumeratedRecord enumeratedRecord1(2); + pcpp::Asn1EnumeratedRecord enumeratedRecord2(3); + pcpp::Asn1IntegerRecord integerRecord1(0); + pcpp::Asn1IntegerRecord integerRecord2(0); + pcpp::Asn1BooleanRecord booleanRecord(false); + + pcpp::Asn1GenericRecord subRecord1(pcpp::Asn1TagClass::ContextSpecific, false, 1, "2.16.840.1.113730.3.3.2.46.1"); + pcpp::Asn1GenericRecord subRecord2(pcpp::Asn1TagClass::ContextSpecific, false, 2, "departmentNumber"); + pcpp::Asn1GenericRecord subRecord3(pcpp::Asn1TagClass::ContextSpecific, false, 3, ">=N4709"); + pcpp::Asn1ConstructedRecord constructedRecord1(pcpp::Asn1TagClass::ContextSpecific, 9, {&subRecord1, &subRecord2, &subRecord3}); + + pcpp::Asn1OctetStringRecord stringSubRecord1("*"); + pcpp::Asn1OctetStringRecord stringSubRecord2("ntsecuritydescriptor"); + pcpp::Asn1SequenceRecord sequenceRecord({&stringSubRecord1, &stringSubRecord2}); + + std::vector controls = { + {"1.2.840.113556.1.4.801", "3003020107"}, + {"1.2.840.113556.1.4.319", "3006020201f40400"} + }; + + pcpp::LdapLayer ldapLayer(6, pcpp::LdapOperationType::SearchRequest, + {&stringRecord, &enumeratedRecord1, &enumeratedRecord2, &integerRecord1, &integerRecord2, &booleanRecord, &constructedRecord1, &sequenceRecord}, + controls); + + auto expectedLdapLayer = ldapPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedLdapLayer); + + PTF_ASSERT_BUF_COMPARE(ldapLayer.getData(), expectedLdapLayer->getData(), expectedLdapLayer->getDataLen()); + } + + // LDAP packet with partial controls + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request1.dat"); + pcpp::Packet ldapPacket(&rawPacket1); + + pcpp::Asn1IntegerRecord integerRecord(3); + pcpp::Asn1OctetStringRecord stringRecord("cn=Administrator,cn=Users,dc=cloudshark-a,dc=example,dc=com"); + uint8_t contextSpecificData[14] = {0x63, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x68, 0x61, 0x72, 0x6b, 0x31, 0x32, 0x33, 0x21}; + pcpp::Asn1GenericRecord contextSpecificRecord(pcpp::Asn1TagClass::ContextSpecific, false, 0, contextSpecificData, 14); + std::vector controls = {{"1.3.6.1.4.1.42.2.27.8.5.1"}}; + + pcpp::LdapLayer ldapLayer(2, pcpp::LdapOperationType::BindRequest, {&integerRecord, &stringRecord, &contextSpecificRecord}, controls); + + auto expectedLdapLayer = ldapPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedLdapLayer); + + PTF_ASSERT_BUF_COMPARE(ldapLayer.getData(), expectedLdapLayer->getData(), expectedLdapLayer->getDataLen()); + } + + // BindRequest with simple authentication + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request1.dat"); + pcpp::Packet bindRequestPacket(&rawPacket1); + + pcpp::LdapBindRequestLayer bindRequestLayer(2, 3, "cn=Administrator,cn=Users,dc=cloudshark-a,dc=example,dc=com", + "cloudshark123!", {{"1.3.6.1.4.1.42.2.27.8.5.1"}}); + + auto expectedBindRequestLayer = bindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedBindRequestLayer); + + PTF_ASSERT_BUF_COMPARE(bindRequestLayer.getData(), expectedBindRequestLayer->getData(), + expectedBindRequestLayer->getDataLen()); + } + + // BindRequest with sasl authentication + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_request2.dat"); + pcpp::Packet bindRequestPacket(&rawPacket1); + + pcpp::LdapBindRequestLayer::SaslAuthentication saslAuthentication { + "GSS-SPNEGO", + {0x60, 0x82, 0x05, 0x1a, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x82, 0x05, 0x0e, 0x30, 0x82, 0x05, 0x0a, 0xa0, 0x24, 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa2, 0x82, 0x04, 0xe0, 0x04, 0x82, 0x04, 0xdc, 0x60, 0x82, 0x04, 0xd8, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x01, 0x00, 0x6e, 0x82, 0x04, 0xc7, 0x30, 0x82, 0x04, 0xc3, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x03, 0x02, 0x01, 0x0e, 0xa2, 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xa3, 0x82, 0x03, 0xe0, + 0x61, 0x82, 0x03, 0xdc, 0x30, 0x82, 0x03, 0xd8, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x15, 0x1b, 0x13, 0x57, 0x32, 0x4b, 0x33, 0x2e, 0x56, 0x4d, 0x4e, 0x45, 0x54, 0x31, 0x2e, 0x56, 0x4d, 0x2e, 0x42, 0x41, 0x53, 0x45, 0xa2, 0x2f, + 0x30, 0x2d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0xa1, 0x26, 0x30, 0x24, 0x1b, 0x04, 0x6c, 0x64, 0x61, 0x70, 0x1b, 0x1c, 0x77, 0x32, 0x6b, 0x33, 0x2d, 0x31, 0x30, 0x31, 0x2e, 0x77, 0x32, 0x6b, 0x33, 0x2e, 0x76, 0x6d, 0x6e, 0x65, 0x74, + 0x31, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65, 0xa3, 0x82, 0x03, 0x87, 0x30, 0x82, 0x03, 0x83, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa1, 0x03, 0x02, 0x01, 0x07, 0xa2, 0x82, 0x03, 0x75, 0x04, 0x82, 0x03, 0x71, 0x6a, 0x61, 0xc8, + 0x86, 0xba, 0x58, 0xd1, 0x62, 0x11, 0x3d, 0xb4, 0x26, 0x8f, 0x77, 0x43, 0xa1, 0x7e, 0xb4, 0x76, 0x18, 0x3b, 0xc0, 0xc5, 0x19, 0xad, 0xde, 0xa7, 0x65, 0x56, 0xa3, 0x70, 0x1d, 0xe3, 0x49, 0x03, 0xe6, 0xbd, 0x3f, 0x3f, 0xdc, 0xa0, + 0xb0, 0x1b, 0xbc, 0xcb, 0x9a, 0x86, 0x93, 0xb2, 0x3f, 0xa8, 0xd1, 0x98, 0x5e, 0x14, 0x92, 0x2e, 0x4c, 0xa1, 0x9b, 0x05, 0xa9, 0x07, 0x69, 0x84, 0x5a, 0x58, 0x58, 0x51, 0x5b, 0xba, 0x4a, 0xf2, 0xd7, 0xe5, 0x9b, 0xfa, 0x86, 0x34, + 0x28, 0x5a, 0x2e, 0x95, 0x4f, 0xb5, 0x18, 0x37, 0x8b, 0x8d, 0x3f, 0x27, 0x44, 0xb9, 0xbb, 0xf8, 0x84, 0x2b, 0x48, 0x07, 0x87, 0x9f, 0xf2, 0x8e, 0x55, 0xbf, 0xba, 0x49, 0x67, 0xe8, 0xc1, 0xd3, 0xb6, 0xc4, 0xe3, 0x58, 0xa5, 0x61, + 0xc5, 0x4a, 0xbb, 0xc1, 0xcb, 0x7c, 0x97, 0xb6, 0x50, 0x3f, 0xe5, 0x9b, 0x7f, 0xee, 0x64, 0x23, 0xdf, 0xfe, 0x66, 0xfe, 0x6d, 0xcb, 0x8a, 0xf0, 0x0e, 0x69, 0xc5, 0x3d, 0x6b, 0x57, 0x6f, 0x55, 0x06, 0x99, 0x04, 0x38, 0x31, 0x0f, + 0xb7, 0xdd, 0x14, 0x68, 0xa3, 0x2f, 0xd8, 0xe0, 0xde, 0xab, 0x40, 0xb1, 0x5e, 0xcf, 0xd4, 0x38, 0x56, 0x83, 0x70, 0x14, 0x0a, 0x1e, 0xda, 0xfe, 0xe7, 0x01, 0xa4, 0xa4, 0xb4, 0xe7, 0xb3, 0xaa, 0xef, 0xdc, 0x4b, 0x1a, 0xff, 0x58, + 0x68, 0xae, 0xfe, 0x5a, 0x36, 0x29, 0x4d, 0x5d, 0xd6, 0x87, 0xd5, 0xa6, 0x49, 0x31, 0x43, 0xd3, 0xad, 0xe8, 0x03, 0x1c, 0x98, 0xd2, 0x8f, 0x6c, 0x7f, 0x3d, 0xce, 0xa4, 0x14, 0x35, 0x13, 0x2f, 0x67, 0x5f, 0x26, 0x94, 0x0d, 0x1f, + 0x69, 0xe5, 0x73, 0xe5, 0xec, 0xe6, 0xed, 0x5a, 0x66, 0x11, 0x1f, 0xf9, 0xf4, 0xb0, 0x2a, 0x8d, 0xdd, 0x19, 0x08, 0x6e, 0x5b, 0x9d, 0xc0, 0xad, 0xc8, 0x6a, 0x0b, 0xc1, 0x23, 0x0f, 0x1b, 0x71, 0x5f, 0xfc, 0x40, 0x04, 0xdf, 0xc4, + 0xa7, 0xd5, 0xf7, 0x8a, 0x4d, 0xc3, 0x1a, 0xbf, 0x83, 0x0a, 0xe6, 0xe3, 0xbf, 0xd2, 0x1c, 0x87, 0xfa, 0x51, 0x96, 0x54, 0x9e, 0x13, 0x0f, 0x6a, 0x08, 0x1b, 0xaf, 0xcf, 0x41, 0x70, 0xae, 0x20, 0x1c, 0x78, 0xa3, 0x82, 0x9a, 0x01, + 0xdb, 0xa5, 0x78, 0xa2, 0xef, 0x96, 0x8f, 0x2a, 0xb6, 0x66, 0x8d, 0x81, 0x14, 0xdf, 0xcc, 0x65, 0xd7, 0x03, 0x8f, 0x55, 0x58, 0xbe, 0x7c, 0xdd, 0x92, 0x46, 0xd5, 0x22, 0x47, 0x91, 0x52, 0x60, 0xa4, 0x0e, 0x59, 0xc4, 0x8b, 0x08, + 0xa1, 0xed, 0x61, 0x42, 0x7f, 0xd3, 0x03, 0x91, 0x7c, 0x6b, 0x34, 0xb7, 0x01, 0xa4, 0xba, 0x9a, 0x38, 0x15, 0xd4, 0x82, 0x8a, 0x22, 0x8c, 0xd2, 0x09, 0xda, 0x13, 0x76, 0x26, 0xe2, 0x02, 0x9a, 0xab, 0xf6, 0xc2, 0x00, 0xbf, 0x7f, + 0xd6, 0x3c, 0xf6, 0xd4, 0x3b, 0xb6, 0x18, 0xb3, 0x1a, 0xc4, 0x8e, 0x09, 0x61, 0x35, 0x89, 0xd7, 0x4a, 0x69, 0x54, 0x2e, 0x90, 0x9c, 0xe0, 0xdc, 0x9c, 0x57, 0xc7, 0x7f, 0x7d, 0x89, 0xb9, 0x66, 0xde, 0x20, 0x00, 0x53, 0xa5, 0x8e, + 0xa5, 0x8f, 0x23, 0x74, 0x51, 0x39, 0x61, 0x63, 0x8a, 0x30, 0xca, 0x49, 0xef, 0x0e, 0xec, 0x67, 0x9d, 0x92, 0x7e, 0x38, 0x5b, 0x5d, 0xa7, 0xd4, 0xd3, 0xc1, 0xa5, 0x91, 0x69, 0xb4, 0x63, 0x0b, 0x87, 0x4a, 0x1d, 0x96, 0x9e, 0x45, + 0xd1, 0xfe, 0x37, 0x82, 0x08, 0x9f, 0x43, 0x85, 0x02, 0x49, 0x55, 0x09, 0x3b, 0x30, 0x8e, 0x19, 0x64, 0xd3, 0x07, 0x91, 0x52, 0x71, 0xaa, 0x88, 0x6c, 0x3d, 0x9b, 0x64, 0xd8, 0x46, 0xc8, 0x8c, 0xa1, 0x34, 0x1f, 0xd2, 0xf7, 0x2b, + 0x76, 0x67, 0x9d, 0x4f, 0x25, 0x8f, 0x64, 0x7b, 0xc0, 0x48, 0x20, 0xe4, 0x27, 0x76, 0xc9, 0xec, 0x0d, 0x01, 0x46, 0x46, 0x52, 0x76, 0x3a, 0x49, 0xd8, 0x22, 0xc9, 0xd2, 0x5b, 0x60, 0x39, 0x03, 0xeb, 0xd6, 0x33, 0x89, 0x52, 0x25, + 0x9b, 0x83, 0xa7, 0x40, 0xa4, 0x20, 0xd6, 0x9d, 0x23, 0xae, 0xbb, 0xdf, 0x06, 0xa9, 0x2d, 0x88, 0xa4, 0x6f, 0xfc, 0xd8, 0xd8, 0x1a, 0x47, 0xb6, 0xec, 0x99, 0xb6, 0xce, 0xa0, 0x48, 0x9c, 0xc8, 0x3e, 0xf1, 0x57, 0x57, 0xc4, 0x05, + 0x3d, 0x53, 0x84, 0x46, 0xf2, 0xe6, 0xb9, 0xeb, 0xa1, 0x2c, 0xe4, 0x96, 0x9b, 0x8d, 0x6d, 0xf9, 0xb3, 0xef, 0x57, 0x4b, 0x7d, 0x40, 0x13, 0x41, 0xc2, 0xf5, 0x55, 0xa0, 0x0f, 0x02, 0x91, 0x64, 0xe5, 0xd3, 0x87, 0x28, 0x2c, 0x0c, + 0x87, 0x91, 0xba, 0x8c, 0x69, 0x81, 0x62, 0x48, 0xe2, 0xe5, 0x44, 0xa9, 0xc1, 0x2b, 0x7a, 0xeb, 0xa6, 0x29, 0xfd, 0xee, 0xa2, 0xe1, 0x11, 0x65, 0x5e, 0x44, 0xb9, 0xc2, 0x15, 0x92, 0x4c, 0x54, 0x55, 0xea, 0xa4, 0xab, 0x32, 0xae, + 0xa1, 0xd9, 0xce, 0xf1, 0xd8, 0x6e, 0x8a, 0xcf, 0x6b, 0x0f, 0xf4, 0xdc, 0xab, 0xaf, 0x4f, 0x0e, 0x2d, 0x9a, 0xe6, 0x5c, 0x8b, 0xb1, 0x06, 0x5e, 0x04, 0x18, 0xff, 0x12, 0xd4, 0x62, 0x69, 0x30, 0x31, 0x59, 0x38, 0xbf, 0xe0, 0x0a, + 0x8d, 0x03, 0xe8, 0xe7, 0x0e, 0x9d, 0xea, 0x9d, 0xc9, 0xff, 0x74, 0x85, 0x4c, 0xbb, 0x4d, 0xbd, 0xf7, 0x00, 0xa6, 0x2e, 0x77, 0xb2, 0x6e, 0x50, 0xb1, 0x3e, 0x2d, 0x39, 0x60, 0xc9, 0x13, 0x36, 0x0c, 0x84, 0xc8, 0x7e, 0x80, 0x1e, + 0xd3, 0xdf, 0x3d, 0xb0, 0xe2, 0x76, 0x04, 0x50, 0x8c, 0xb7, 0x30, 0xc5, 0xa0, 0x52, 0xc0, 0x68, 0xab, 0xe5, 0x82, 0x6b, 0x01, 0xbe, 0x9f, 0x62, 0xe3, 0x3b, 0x9a, 0xf8, 0xed, 0xb6, 0x66, 0x7c, 0x57, 0xcb, 0x1a, 0xa8, 0x79, 0x74, + 0x3b, 0x77, 0xa7, 0x43, 0x2f, 0x75, 0xfe, 0x3a, 0xe2, 0x11, 0xf9, 0x6a, 0xf4, 0x1a, 0xde, 0xf1, 0xe1, 0xc5, 0x07, 0x25, 0x6f, 0xe5, 0xfa, 0x2b, 0xcc, 0xab, 0xe5, 0x2c, 0xf8, 0x21, 0x6d, 0x34, 0x10, 0xe6, 0x37, 0x85, 0x06, 0xd4, + 0x27, 0x34, 0x34, 0x58, 0x33, 0x2d, 0x15, 0x3a, 0x77, 0xa1, 0x62, 0xc4, 0xc5, 0xf1, 0x8d, 0x9f, 0x31, 0xb0, 0xc1, 0x42, 0x88, 0x0c, 0xad, 0x22, 0x29, 0x98, 0x17, 0x20, 0x61, 0x5a, 0xb2, 0x6b, 0x7c, 0x13, 0x44, 0x2e, 0x43, 0x17, + 0x8a, 0xad, 0xee, 0x43, 0x65, 0x10, 0xc9, 0x1b, 0xc9, 0xd5, 0xd7, 0x35, 0xeb, 0x94, 0x53, 0xcf, 0x39, 0xce, 0xf5, 0x12, 0x0e, 0x28, 0x60, 0x37, 0x75, 0xf0, 0x48, 0x3f, 0x01, 0xc3, 0xc4, 0x8b, 0x5b, 0x06, 0x0c, 0xa7, 0xf3, 0xa5, + 0x4d, 0x7c, 0x7c, 0x99, 0xa4, 0x81, 0xc9, 0x30, 0x81, 0xc6, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa2, 0x81, 0xbe, 0x04, 0x81, 0xbb, 0x03, 0xab, 0x65, 0x67, 0x60, 0xa3, 0x51, 0x2f, 0xec, 0xc7, 0x03, 0x2d, 0xa8, 0xb2, 0x01, 0x46, 0x59, + 0xf0, 0xfb, 0x34, 0xeb, 0x76, 0xb4, 0x61, 0xe4, 0x04, 0x4d, 0xa2, 0x4d, 0x16, 0xd4, 0x58, 0xe3, 0xe1, 0xc5, 0x89, 0x19, 0xc7, 0x4c, 0x4c, 0x07, 0x20, 0xaa, 0xfb, 0x87, 0xa9, 0x48, 0x15, 0x23, 0x72, 0xa2, 0x48, 0x3a, 0x4d, 0x1a, + 0xe9, 0xb9, 0x5b, 0x85, 0x8a, 0x52, 0xab, 0xaa, 0x94, 0xe7, 0xaa, 0x64, 0x1a, 0x8b, 0x99, 0x7d, 0x7e, 0x6c, 0x6e, 0x57, 0x0b, 0x59, 0x08, 0xcc, 0x54, 0x91, 0x55, 0xf5, 0xe6, 0xf1, 0x10, 0xc9, 0x8d, 0x64, 0x89, 0x78, 0x72, 0x7a, + 0xba, 0xe3, 0x92, 0x1d, 0xa5, 0x2a, 0x4c, 0x1f, 0xd7, 0x6b, 0xeb, 0x12, 0x1b, 0xf3, 0x39, 0x6b, 0xe8, 0xf9, 0x8e, 0x4a, 0xcf, 0x1e, 0xbf, 0xc3, 0xb6, 0xfb, 0x7a, 0x13, 0x54, 0xc1, 0x21, 0x87, 0x3e, 0x59, 0x18, 0x5d, 0xb9, 0x00, + 0x30, 0x08, 0x4d, 0x97, 0x86, 0x47, 0x98, 0xd7, 0x9e, 0xb9, 0xdf, 0x30, 0x75, 0x6c, 0xa1, 0xfa, 0xa7, 0xa8, 0x08, 0x80, 0xf7, 0x4f, 0x7d, 0x93, 0x64, 0x2d, 0x9c, 0xeb, 0x5e, 0x01, 0x28, 0xce, 0xd6, 0xab, 0x09, 0x6a, 0x4f, 0x01, + 0x5e, 0x5a, 0x03, 0x2b, 0x42, 0x70, 0x23, 0x1e, 0x7f, 0xf1, 0xbc, 0xd0, 0x87, 0xe8, 0xb5, 0x27, 0x02, 0x7d} + }; + pcpp::LdapBindRequestLayer bindRequestLayer(2, 3, "", saslAuthentication); + + auto expectedBindRequestLayer = bindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedBindRequestLayer); + + PTF_ASSERT_EQUAL(bindRequestLayer.getLdapOperationAsn1Record()->getSubRecords().size(), expectedBindRequestLayer->getLdapOperationAsn1Record()->getSubRecords().size()); + PTF_ASSERT_TRUE(std::equal(bindRequestLayer.getLdapOperationAsn1Record()->getSubRecords().begin(), + bindRequestLayer.getLdapOperationAsn1Record()->getSubRecords().end(), + expectedBindRequestLayer->getLdapOperationAsn1Record()->getSubRecords().begin(), + [](pcpp::Asn1Record* elem1, pcpp::Asn1Record* elem2) { return elem1->encode() == elem2->encode();})); + } + + // BindResponse with server sasl credentials + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_response1.dat"); + pcpp::Packet bindResponsePacket(&rawPacket1); + + std::vector serverSaslCredentials = { + 0xa1, 0x81, 0xa1, 0x30, 0x81, 0x9e, 0xa0, 0x03, 0x0a, 0x01, 0x00, 0xa1, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, 0xa2, 0x81, 0x89, 0x04, 0x81, 0x86, 0x60, 0x81, 0x83, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x02, 0x00, 0x6f, 0x74, 0x30, 0x72, 0xa0, 0x03, 0x02, 0x01, 0x05, 0xa1, 0x03, 0x02, 0x01, 0x0f, 0xa2, 0x66, 0x30, 0x64, 0xa0, 0x03, 0x02, 0x01, 0x17, 0xa2, 0x5d, 0x04, 0x5b, 0x4a, 0x9f, 0x10, + 0xab, 0x89, 0x96, 0xfa, 0x43, 0xf2, 0xfb, 0x40, 0x92, 0xa7, 0x6c, 0xc3, 0xfa, 0x6c, 0x1f, 0x00, 0x11, 0x67, 0xfa, 0xc9, 0x04, 0xda, 0xb0, 0x67, 0xf5, 0xf2, 0xda, 0x59, 0xa7, 0x54, 0x90, 0x57, 0xbd, 0x3e, 0xb4, 0x6c, 0xb4, 0x67, + 0xfd, 0x3b, 0x01, 0xd7, 0x3f, 0x50, 0x51, 0xaa, 0x63, 0x2e, 0xd8, 0xd6, 0xa6, 0xe5, 0x81, 0xbb, 0xab, 0x17, 0x80, 0xfa, 0xab, 0xac, 0x51, 0x52, 0x84, 0x13, 0x9c, 0xfb, 0x44, 0xc2, 0x04, 0xae, 0x1e, 0xc2, 0x5a, 0x2d, 0x58, 0x90, + 0x9d, 0x22, 0xff, 0x52, 0x34, 0x9e, 0x6d, 0x2e, 0x4d, 0x83, 0x5b, 0x98}; + + pcpp::LdapBindResponseLayer bindResponseLayer(215, pcpp::LdapResultCode::Success, "", "", {}, serverSaslCredentials); + + auto expectedBindResponseLayer = bindResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedBindResponseLayer); + + PTF_ASSERT_EQUAL(bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().size(), expectedBindResponseLayer->getLdapOperationAsn1Record()->getSubRecords().size()); + + for (int i = 0; i < 3; i++) + { + PTF_ASSERT_EQUAL(bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().at(i)->toString(), + expectedBindResponseLayer->getLdapOperationAsn1Record()->getSubRecords().at(i)->toString()); + } + + auto actualServerSaslCredentialsRecord = bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().at(3)->castAs(); + auto expectedServerSaslCredentialsRecord = expectedBindResponseLayer->getLdapOperationAsn1Record()->getSubRecords().at(3)->castAs(); + PTF_ASSERT_BUF_COMPARE(actualServerSaslCredentialsRecord->getValue(), expectedServerSaslCredentialsRecord->getValue(), expectedServerSaslCredentialsRecord->getValueLength()) + } + + // BindResponse without server sasl credentials + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_bind_response2.dat"); + pcpp::Packet bindResponsePacket(&rawPacket1); + + pcpp::LdapBindResponseLayer bindResponseLayer(2, pcpp::LdapResultCode::Success, "", ""); + + auto expectedBindResponseLayer = bindResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedBindResponseLayer); + + PTF_ASSERT_EQUAL(bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().size(), expectedBindResponseLayer->getLdapOperationAsn1Record()->getSubRecords().size()); + PTF_ASSERT_TRUE(std::equal(bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().begin(), + bindResponseLayer.getLdapOperationAsn1Record()->getSubRecords().end(), + expectedBindResponseLayer->getLdapOperationAsn1Record()->getSubRecords().begin(), + [](pcpp::Asn1Record* elem1, pcpp::Asn1Record* elem2) { return elem1->encode() == elem2->encode();})); + } + + // UnbindRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_unbind_request.dat"); + pcpp::Packet unbindRequestPacket(&rawPacket1); + + pcpp::LdapUnbindRequestLayer unbindRequestLayer(3); + + auto expectedUnbindRequestLayer = unbindRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedUnbindRequestLayer); + + PTF_ASSERT_BUF_COMPARE(unbindRequestLayer.getData(), expectedUnbindRequestLayer->getData(), + expectedUnbindRequestLayer->getDataLen()); + } + + // SearchRequest + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_request2.dat"); + pcpp::Packet searchRequestPacket(&rawPacket1); + + pcpp::Asn1OctetStringRecord filterSubRecord1("objectClass"); + pcpp::Asn1OctetStringRecord filterSubRecord2("subschema"); + pcpp::Asn1ConstructedRecord filterRecord(pcpp::Asn1TagClass::ContextSpecific, 3, {&filterSubRecord1, &filterSubRecord2}); + + std::vector attributes = { + "objectClasses", + "attributeTypes", + "ldapSyntaxes", + "matchingRules", + "matchingRuleUse", + "dITContentRules", + "dITStructureRules", + "nameForms", + "createTimestamp", + "modifyTimestamp", + "*", + "+" + }; + + pcpp::LdapSearchRequestLayer searchRequestLayer( + 9, "cn=schema", pcpp::LdapSearchRequestLayer::SearchRequestScope::BaseObject, + pcpp::LdapSearchRequestLayer::DerefAliases::DerefAlways, + 0, 0, false, &filterRecord, attributes); + + auto expectedSearchRequestLayer = searchRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedSearchRequestLayer); + + PTF_ASSERT_BUF_COMPARE(searchRequestLayer.getData(), expectedSearchRequestLayer->getData(), + expectedSearchRequestLayer->getDataLen()); + } + + // SearchResultEntry + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_entry.dat"); + pcpp::Packet searchResultEntryPacket(&rawPacket1); + + std::vector attributes = { + {"objectclass", {"inetOrgPerson", "organizationalPerson", "person", "top"}}, + {"sn", {"Young"}}, + {"cn", {"b.smith"}}, + {"givenname", {"Beatrix"}} + }; + + pcpp::LdapSearchResultEntryLayer searchResultEntryLayer(16, "cn=b.smith,ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org", attributes); + + auto expectedSearchResultEntryLayer = searchResultEntryPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedSearchResultEntryLayer); + + PTF_ASSERT_BUF_COMPARE(searchResultEntryLayer.getData(), expectedSearchResultEntryLayer->getData(), + expectedSearchResultEntryLayer->getDataLen()); + } + + // LdapSearchResultDoneLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_search_result_done.dat"); + pcpp::Packet searchResultDonePacket(&rawPacket1); + + pcpp::LdapSearchResultDoneLayer searchResultDoneLayer(8, pcpp::LdapResultCode::Success, "", ""); + + auto expectedSearchResultDoneLayer = searchResultDonePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedSearchResultDoneLayer); + + PTF_ASSERT_BUF_COMPARE(searchResultDoneLayer.getData(), expectedSearchResultDoneLayer->getData(), + expectedSearchResultDoneLayer->getDataLen()); + } + + // LdapModifyResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_modify_response.dat"); + pcpp::Packet modifyResponsePacket(&rawPacket1); + + pcpp::LdapModifyResponseLayer modifyResponseLayer(14, pcpp::LdapResultCode::NoSuchObject, "", ""); + + auto expectedModifyResponseLayer = modifyResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedModifyResponseLayer); + + PTF_ASSERT_BUF_COMPARE(modifyResponseLayer.getData(), expectedModifyResponseLayer->getData(), + expectedModifyResponseLayer->getDataLen()); + } + + // LdapAddResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_add_response.dat"); + pcpp::Packet addResponsePacket(&rawPacket1); + + pcpp::LdapAddResponseLayer addResponseLayer(27, pcpp::LdapResultCode::Success, "", ""); + + auto expectedAddResponseLayer = addResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedAddResponseLayer); + + PTF_ASSERT_BUF_COMPARE(addResponseLayer.getData(), expectedAddResponseLayer->getData(), + expectedAddResponseLayer->getDataLen()); + } + + // LdapDeleteResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_delete_response.dat"); + pcpp::Packet deleteResponsePacket(&rawPacket1); + + std::vector referral = { + "ldap://ldap.example.com/dc=example,dc=com", + "ldap://ldap.example.com/dc=example,dc=com?objectClass?one" + }; + + pcpp::LdapDeleteResponseLayer deleteResponseLayer(27, pcpp::LdapResultCode::NoSuchObject, "ou=People,dc=example,dc=com", + "LDAP: error code 32 - No such object", referral); + + auto expectedDeleteResponseLayer = deleteResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedDeleteResponseLayer); + + PTF_ASSERT_BUF_COMPARE(deleteResponseLayer.getData(), expectedDeleteResponseLayer->getData(), + expectedDeleteResponseLayer->getDataLen()); + } + + // LdapModifyDNResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_modify_dn_response.dat"); + pcpp::Packet modifyDNResponsePacket(&rawPacket1); + + pcpp::LdapModifyDNResponseLayer modifyDNResponseLayer(15, pcpp::LdapResultCode::NoSuchObject, "ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org", ""); + + auto expectedModifyDNResponseLayer = modifyDNResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedModifyDNResponseLayer); + + PTF_ASSERT_BUF_COMPARE(modifyDNResponseLayer.getData(), expectedModifyDNResponseLayer->getData(), + expectedModifyDNResponseLayer->getDataLen()); + } + + // LdapCompareResponseLayer + { + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/ldap_compare_response.dat"); + pcpp::Packet compareResponsePacket(&rawPacket1); + + pcpp::LdapCompareResponseLayer compareResponseLayer(28, pcpp::LdapResultCode::CompareFalse, "", ""); + + auto expectedCompareResponseLayer = compareResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(expectedCompareResponseLayer); + + PTF_ASSERT_BUF_COMPARE(compareResponseLayer.getData(), expectedCompareResponseLayer->getData(), + expectedCompareResponseLayer->getDataLen()); + } +} // LdapCreationTest diff --git a/Tests/Packet++Test/Tests/PacketTests.cpp b/Tests/Packet++Test/Tests/PacketTests.cpp index ec9015b29c..cea40f0502 100644 --- a/Tests/Packet++Test/Tests/PacketTests.cpp +++ b/Tests/Packet++Test/Tests/PacketTests.cpp @@ -394,8 +394,8 @@ PTF_TEST_CASE(CopyLayerAndPacketTest) PTF_ASSERT_TRUE(sampleTcpPacketWithOptions.getLayerOfType()->getData() != tcpLayer.getData()); PTF_ASSERT_BUF_COMPARE(sampleTcpPacketWithOptions.getLayerOfType()->getData(), tcpLayer.getData(), sampleTcpPacketWithOptions.getLayerOfType()->getDataLen()); PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), sampleTcpPacketWithOptions.getLayerOfType()->getTcpOptionCount()); - PTF_ASSERT_TRUE(sampleTcpPacketWithOptions.getLayerOfType()->getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP).getRecordBasePtr() != tcpLayer.getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP).getRecordBasePtr()); - PTF_ASSERT_TRUE(sampleTcpPacketWithOptions.getLayerOfType()->getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP) == tcpLayer.getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP)); + PTF_ASSERT_TRUE(sampleTcpPacketWithOptions.getLayerOfType()->getTcpOption(pcpp::TcpOptionEnumType::Timestamp).getRecordBasePtr() != tcpLayer.getTcpOption(pcpp::TcpOptionEnumType::Timestamp).getRecordBasePtr()); + PTF_ASSERT_TRUE(sampleTcpPacketWithOptions.getLayerOfType()->getTcpOption(pcpp::TcpOptionEnumType::Timestamp) == tcpLayer.getTcpOption(pcpp::TcpOptionEnumType::Timestamp)); //HttpLayer copy c'tor test diff --git a/Tests/Packet++Test/Tests/SllNullLoopbackTests.cpp b/Tests/Packet++Test/Tests/SllNullLoopbackTests.cpp index d06719c591..3cca58180d 100644 --- a/Tests/Packet++Test/Tests/SllNullLoopbackTests.cpp +++ b/Tests/Packet++Test/Tests/SllNullLoopbackTests.cpp @@ -58,9 +58,9 @@ PTF_TEST_CASE(SllPacketCreationTest) tcpLayer.getTcpHeader()->ackNumber = htobe32(0x7633e977); tcpLayer.getTcpHeader()->ackFlag = 1; tcpLayer.getTcpHeader()->windowSize = htobe16(4098); - PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NOP)).isNotNull()); - PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NOP)).isNotNull()); - pcpp::TcpOption tsOption = tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::PCPP_TCPOPT_TIMESTAMP, nullptr, PCPP_TCPOLEN_TIMESTAMP-2)); + PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NopEolOptionEnumType::Nop)).isNotNull()); + PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NopEolOptionEnumType::Nop)).isNotNull()); + pcpp::TcpOption tsOption = tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionEnumType::Timestamp, nullptr, PCPP_TCPOLEN_TIMESTAMP-2)); PTF_ASSERT_TRUE(tsOption.isNotNull()); tsOption.setValue(htobe32(0x0402383b)); tsOption.setValue(htobe32(0x03ff37f5), 4); diff --git a/Tests/Packet++Test/Tests/TcpTests.cpp b/Tests/Packet++Test/Tests/TcpTests.cpp index 3c941a7054..f50e28cd12 100644 --- a/Tests/Packet++Test/Tests/TcpTests.cpp +++ b/Tests/Packet++Test/Tests/TcpTests.cpp @@ -8,6 +8,12 @@ #include "PayloadLayer.h" #include "SystemUtils.h" #include "PacketUtils.h" +#include "DeprecationUtils.h" + +// TODO: remove these macros, when deprecated code is gone +DISABLE_WARNING_PUSH +DISABLE_WARNING_DEPRECATED + PTF_TEST_CASE(TcpPacketNoOptionsParsing) { @@ -44,8 +50,14 @@ PTF_TEST_CASE(TcpPacketNoOptionsParsing) // TCP options PTF_ASSERT_EQUAL(tcpLayer->getTcpOptionCount(), 0); + + //TODO: remove deprecated PTF_ASSERT_TRUE(tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_NOP).isNull()); PTF_ASSERT_TRUE(tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP).isNull()); + //end deprecated + + PTF_ASSERT_TRUE(tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Nop).isNull()); + PTF_ASSERT_TRUE(tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Timestamp).isNull()); pcpp::Layer* afterTcpLayer = tcpLayer->getNextLayer(); PTF_ASSERT_NOT_NULL(afterTcpLayer); @@ -77,18 +89,28 @@ PTF_TEST_CASE(TcpPacketWithOptionsParsing) // TCP options PTF_ASSERT_EQUAL(tcpLayer->getTcpOptionCount(), 3); + + //TODO: remove deprecated + PTF_ASSERT_TRUE(!tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_NOP).isNull()); pcpp::TcpOption timestampOptionData = tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_TIMESTAMP); PTF_ASSERT_TRUE(!timestampOptionData.isNull()); - PTF_ASSERT_TRUE(!tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_NOP).isNull()); PTF_ASSERT_EQUAL(timestampOptionData.getTotalSize(), 10); uint32_t tsValue = timestampOptionData.getValueAs(); uint32_t tsEchoReply = timestampOptionData.getValueAs(4); PTF_ASSERT_EQUAL(tsValue, htobe32(195102)); PTF_ASSERT_EQUAL(tsEchoReply, htobe32(3555729271UL)); + //end deprecated + + PTF_ASSERT_TRUE(!tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Nop).isNull()); + pcpp::TcpOption timestampOptionData2 = tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Timestamp); + PTF_ASSERT_TRUE(!timestampOptionData2.isNull()); + PTF_ASSERT_EQUAL(timestampOptionData2.getTotalSize(), 10); + uint32_t tsValue2 = timestampOptionData2.getValueAs(); + uint32_t tsEchoReply2 = timestampOptionData2.getValueAs(4); + PTF_ASSERT_EQUAL(tsValue2, htobe32(195102)); + PTF_ASSERT_EQUAL(tsEchoReply2, htobe32(3555729271UL)); } // TcpPacketWithOptionsParsing - - PTF_TEST_CASE(TcpPacketWithOptionsParsing2) { timeval time; @@ -102,6 +124,8 @@ PTF_TEST_CASE(TcpPacketWithOptionsParsing2) PTF_ASSERT_NOT_NULL(tcpLayer); PTF_ASSERT_EQUAL(tcpLayer->getTcpOptionCount(), 5); + + //TODO: remove deprecated pcpp::TcpOption mssOption = tcpLayer->getTcpOption(pcpp::TCPOPT_MSS); pcpp::TcpOption sackPermOption = tcpLayer->getTcpOption(pcpp::TCPOPT_SACK_PERM); pcpp::TcpOption windowScaleOption = tcpLayer->getTcpOption(pcpp::PCPP_TCPOPT_WINDOW); @@ -122,7 +146,30 @@ PTF_TEST_CASE(TcpPacketWithOptionsParsing2) PTF_ASSERT_EQUAL(sackPermOption.getValueAs(), 0); PTF_ASSERT_EQUAL(mssOption.getValueAs(), 0); PTF_ASSERT_EQUAL(mssOption.getValueAs(1), 0); - + //end deprecated + + pcpp::TcpOption mssOption2 = tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Mss); + pcpp::TcpOption sackPermOption2 = tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::SackPerm); + pcpp::TcpOption windowScaleOption2 = tcpLayer->getTcpOption(pcpp::TcpOptionEnumType::Window); + PTF_ASSERT_TRUE(mssOption2.isNotNull()); + PTF_ASSERT_TRUE(sackPermOption2.isNotNull()); + PTF_ASSERT_TRUE(windowScaleOption2.isNotNull()); + + PTF_ASSERT_EQUAL(mssOption2.getTcpOptionEnumType(), pcpp::TcpOptionEnumType::Mss, enumclass); + PTF_ASSERT_EQUAL(sackPermOption2.getTcpOptionEnumType(), pcpp::TcpOptionEnumType::SackPerm, enumclass); + PTF_ASSERT_EQUAL(windowScaleOption2.getTcpOptionEnumType(), pcpp::TcpOptionEnumType::Window, enumclass); + + PTF_ASSERT_EQUAL(mssOption2.getTotalSize(), 4); + PTF_ASSERT_EQUAL(sackPermOption2.getTotalSize(), 2); + PTF_ASSERT_EQUAL(windowScaleOption2.getTotalSize(), 3); + + PTF_ASSERT_EQUAL(mssOption2.getValueAs(), htobe16(1460)); + PTF_ASSERT_EQUAL(windowScaleOption2.getValueAs(), 4); + PTF_ASSERT_EQUAL(sackPermOption2.getValueAs(), 0); + PTF_ASSERT_EQUAL(mssOption2.getValueAs(), 0); + PTF_ASSERT_EQUAL(mssOption2.getValueAs(1), 0); + + //TODO: remove deprecated pcpp::TcpOption curOpt = tcpLayer->getFirstTcpOption(); PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionType() == pcpp::TCPOPT_MSS); curOpt = tcpLayer->getNextTcpOption(curOpt); @@ -133,6 +180,18 @@ PTF_TEST_CASE(TcpPacketWithOptionsParsing2) PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionType() == pcpp::PCPP_TCPOPT_NOP); curOpt = tcpLayer->getNextTcpOption(curOpt); PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionType() == pcpp::PCPP_TCPOPT_WINDOW); + //end deprecated + + curOpt = tcpLayer->getFirstTcpOption(); + PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionEnumType() == pcpp::TcpOptionEnumType::Mss); + curOpt = tcpLayer->getNextTcpOption(curOpt); + PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionEnumType() == pcpp::TcpOptionEnumType::SackPerm); + curOpt = tcpLayer->getNextTcpOption(curOpt); + PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionEnumType() == pcpp::TcpOptionEnumType::Timestamp); + curOpt = tcpLayer->getNextTcpOption(curOpt); + PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionEnumType() == pcpp::TcpOptionEnumType::Nop); + curOpt = tcpLayer->getNextTcpOption(curOpt); + PTF_ASSERT_TRUE(curOpt.isNotNull() && curOpt.getTcpOptionEnumType() == pcpp::TcpOptionEnumType::Window); curOpt = tcpLayer->getNextTcpOption(curOpt); PTF_ASSERT_TRUE(curOpt.isNull()); } // TcpPacketWithOptionsParsing2 @@ -152,8 +211,6 @@ PTF_TEST_CASE(TcpMalformedPacketParsing) PTF_ASSERT_NULL(badTcpPacket.getLayerOfType()); } // TcpMalformedPacketParsing - - PTF_TEST_CASE(TcpPacketCreation) { pcpp::MacAddress srcMac("30:46:9a:23:fb:fa"); @@ -177,7 +234,11 @@ PTF_TEST_CASE(TcpPacketCreation) PTF_ASSERT_EQUAL(tcpLayer.getHeaderLen(), 24); PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::PCPP_TCPOPT_TIMESTAMP, nullptr, PCPP_TCPOLEN_TIMESTAMP-2)).isNotNull()); PTF_ASSERT_EQUAL(tcpLayer.getHeaderLen(), 32); - PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 3); + + PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NopEolOptionEnumType::Nop)).isNotNull()); + PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionEnumType::Window, nullptr, PCPP_TCPOLEN_WINDOW-2)).isNotNull()); + PTF_ASSERT_EQUAL(tcpLayer.getHeaderLen(), 36); + PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 5); uint8_t payloadData[9] = { 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; pcpp::PayloadLayer payloadLayer(payloadData, 9); @@ -195,7 +256,12 @@ PTF_TEST_CASE(TcpPacketCreation) tsOption.setValue(tsValue); tsOption.setValue(tsEchoReply, 4); - PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 3); + uint8_t windowScaleFactor = 2; + pcpp::TcpOption windowOption = tcpLayer.getTcpOption(pcpp::TcpOptionEnumType::Window); + PTF_ASSERT_TRUE(windowOption.isNotNull()); + windowOption.setValue(windowScaleFactor); + + PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 5); tcpPacket.computeCalculateFields(); @@ -262,15 +328,15 @@ PTF_TEST_CASE(TcpPacketCreation2) pcpp::TcpOption qsOption = tcpLayer.addTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TCPOPT_QS, nullptr, PCPP_TCPOLEN_QS), pcpp::TCPOPT_MSS); PTF_ASSERT_TRUE(qsOption.isNotNull()); PTF_ASSERT_TRUE(qsOption.setValue(htobe32(9999))); - PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TCPOPT_SNACK, (uint32_t)htobe32(1000))).isNotNull()); - PTF_ASSERT_TRUE(tcpLayer.addTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NOP), pcpp::PCPP_TCPOPT_TIMESTAMP).isNotNull()); + PTF_ASSERT_TRUE(tcpLayer.addTcpOption(pcpp::TcpOptionBuilder(pcpp::TcpOptionEnumType::Snack, static_cast(htobe32(1000)))).isNotNull()); + PTF_ASSERT_TRUE(tcpLayer.insertTcpOptionAfter(pcpp::TcpOptionBuilder(pcpp::TcpOptionBuilder::NopEolOptionEnumType::Nop), pcpp::TcpOptionEnumType::Timestamp).isNotNull()); PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 8); - PTF_ASSERT_TRUE(tcpLayer.removeTcpOption(pcpp::TCPOPT_QS)); + PTF_ASSERT_TRUE(tcpLayer.removeTcpOption(pcpp::TcpOptionEnumType::Qs)); PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 7); PTF_ASSERT_TRUE(tcpLayer.removeTcpOption(pcpp::TCPOPT_SNACK)); - PTF_ASSERT_TRUE(tcpLayer.removeTcpOption(pcpp::PCPP_TCPOPT_NOP)); + PTF_ASSERT_TRUE(tcpLayer.removeTcpOption(pcpp::TcpOptionEnumType::Nop)); PTF_ASSERT_EQUAL(tcpLayer.getTcpOptionCount(), 5); PTF_ASSERT_BUF_COMPARE(tcpPacket.getRawPacket()->getRawData(), buffer1, bufferLength1); @@ -296,7 +362,7 @@ PTF_TEST_CASE(TcpChecksumInvalidRead) m[2] = 0xF3; pcpp::ScalarBuffer vec[1]; - vec[0].buffer = (uint16_t*)m; + vec[0].buffer = reinterpret_cast(m); vec[0].len = 3; uint16_t c = pcpp::computeChecksum(vec, 1); @@ -331,3 +397,4 @@ PTF_TEST_CASE(TcpChecksumMultiBuffer) c = pcpp::computeChecksum(vec, 4); PTF_ASSERT_EQUAL(c, 0); } // TcpChecksumInvalidRead +DISABLE_WARNING_POP diff --git a/Tests/Packet++Test/Utils/TestUtils.cpp b/Tests/Packet++Test/Utils/TestUtils.cpp index d2e8c1f238..03b98b21aa 100644 --- a/Tests/Packet++Test/Utils/TestUtils.cpp +++ b/Tests/Packet++Test/Utils/TestUtils.cpp @@ -103,7 +103,7 @@ void savePacketToPcap(pcpp::Packet& packet, const std::string &fileName) hdr.ts.tv_usec = 0; /* ms */ hdr.caplen = hdr.len = packet.getRawPacket()->getRawDataLen(); /* write single IP packet */ - pcap_dump((u_char*)d, &hdr, packet.getRawPacketReadOnly()->getRawData()); + pcap_dump(static_cast(d), &hdr, packet.getRawPacketReadOnly()->getRawData()); /* finish up */ pcap_dump_close(d); diff --git a/Tests/Packet++Test/Utils/TestUtils.h b/Tests/Packet++Test/Utils/TestUtils.h index fa8e420091..a763ca6163 100644 --- a/Tests/Packet++Test/Utils/TestUtils.h +++ b/Tests/Packet++Test/Utils/TestUtils.h @@ -33,11 +33,11 @@ void testSetUp(); #define READ_FILE_AND_CREATE_PACKET(num, filename) \ READ_FILE_INTO_BUFFER(num, filename); \ - pcpp::RawPacket rawPacket##num((const uint8_t*)buffer##num, bufferLength##num, time, true) + pcpp::RawPacket rawPacket##num(static_cast(buffer##num), bufferLength##num, time, true) #define READ_FILE_AND_CREATE_PACKET_LINKTYPE(num, filename, linktype) \ READ_FILE_INTO_BUFFER(num, filename); \ - pcpp::RawPacket rawPacket##num((const uint8_t*)buffer##num, bufferLength##num, time, true, linktype) + pcpp::RawPacket rawPacket##num(static_cast(buffer##num), bufferLength##num, time, true, linktype) #ifdef PCPP_TESTS_DEBUG void savePacketToPcap(pcpp::Packet& packet, const std::string &fileName); diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 1138a235a6..937bf62707 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -325,5 +325,11 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(SmtpCreationTests, "smtp"); PTF_RUN_TEST(SmtpEditTests, "smtp"); + PTF_RUN_TEST(Asn1DecodingTest, "asn1"); + PTF_RUN_TEST(Asn1EncodingTest, "asn1"); + + PTF_RUN_TEST(LdapParsingTest, "ldap"); + PTF_RUN_TEST(LdapCreationTest, "ldap"); + PTF_END_RUNNING_TESTS; } diff --git a/Tests/Pcap++Test/Common/PcapFileNamesDef.h b/Tests/Pcap++Test/Common/PcapFileNamesDef.h index 9fd550651b..8dfd584c73 100644 --- a/Tests/Pcap++Test/Common/PcapFileNamesDef.h +++ b/Tests/Pcap++Test/Common/PcapFileNamesDef.h @@ -3,6 +3,7 @@ #define EXAMPLE_PCAP_WRITE_PATH "PcapExamples/example_copy.pcap" #define EXAMPLE_PCAP_PATH "PcapExamples/example.pcap" #define EXAMPLE2_PCAP_PATH "PcapExamples/example2.pcap" +#define EXAMPLE_PCAP_IPV6_PATH "PcapExamples/ICMPv6_echos.cap" #define EXAMPLE_PCAP_HTTP_REQUEST "PcapExamples/4KHttpRequests.pcap" #define EXAMPLE_PCAP_HTTP_RESPONSE "PcapExamples/650HttpResponses.pcap" #define EXAMPLE_PCAP_VLAN "PcapExamples/VlanPackets.pcap" @@ -28,3 +29,5 @@ #define EXAMPLE_SOLARIS_SNOOP "PcapExamples/solaris.snoop" #define SLL2_PCAP_PATH "PcapExamples/sll2.pcap" #define SLL2_PCAP_WRITE_PATH "PcapExamples/sll2_copy.pcap" +#define EXAMPLE_PCAP_MICRO_PATH "PcapExamples/microsecs.pcap" +#define EXAMPLE_PCAP_NANO_PATH "PcapExamples/nanosecs.pcap" diff --git a/Tests/Pcap++Test/PcapExamples/ICMPv6_echos.cap b/Tests/Pcap++Test/PcapExamples/ICMPv6_echos.cap new file mode 100644 index 0000000000..8a005836e5 Binary files /dev/null and b/Tests/Pcap++Test/PcapExamples/ICMPv6_echos.cap differ diff --git a/Tests/Pcap++Test/TestDefinition.h b/Tests/Pcap++Test/TestDefinition.h index 16e4d3a68d..29dbb1298f 100644 --- a/Tests/Pcap++Test/TestDefinition.h +++ b/Tests/Pcap++Test/TestDefinition.h @@ -18,6 +18,7 @@ PTF_TEST_CASE(TestLoggerMultiThread); // Implemented in FileTests.cpp PTF_TEST_CASE(TestPcapFileReadWrite); +PTF_TEST_CASE(TestPcapFilePrecision); PTF_TEST_CASE(TestPcapSllFileReadWrite); PTF_TEST_CASE(TestPcapSll2FileReadWrite); PTF_TEST_CASE(TestPcapRawIPFileReadWrite); diff --git a/Tests/Pcap++Test/Tests/FileTests.cpp b/Tests/Pcap++Test/Tests/FileTests.cpp index 408b6f7627..0647c714f8 100644 --- a/Tests/Pcap++Test/Tests/FileTests.cpp +++ b/Tests/Pcap++Test/Tests/FileTests.cpp @@ -3,6 +3,7 @@ #include "Packet.h" #include "PcapFileDevice.h" #include "../Common/PcapFileNamesDef.h" +#include #include @@ -103,6 +104,79 @@ PTF_TEST_CASE(TestPcapFileReadWrite) PTF_ASSERT_FALSE(readerDev2.isOpened()); } // TestPcapFileReadWrite +PTF_TEST_CASE(TestPcapFilePrecision) +{ + std::array testPayload = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + pcpp::RawPacket rawPacketNano(testPayload.data(), testPayload.size(), timespec({1, 1234}), false); // 1.000001234 + pcpp::RawPacket rawPacketMicro(testPayload.data(), testPayload.size(), timeval({1, 2}), false); // 1.000002000 + + // Writer precision support should equal to reader precision support + PTF_ASSERT_EQUAL(pcpp::PcapFileWriterDevice::isNanoSecondPrecisionSupported(), pcpp::PcapFileReaderDevice::isNanoSecondPrecisionSupported()); + + // Write nano precision file + pcpp::PcapFileWriterDevice writerDevNano(EXAMPLE_PCAP_NANO_PATH, pcpp::LINKTYPE_ETHERNET, true); + PTF_ASSERT_EQUAL(writerDevNano.getTimestampPrecision(), pcpp::PcapFileWriterDevice::isNanoSecondPrecisionSupported() + ? pcpp::FileTimestampPrecision::Nanoseconds + : pcpp::FileTimestampPrecision::Microseconds, enumclass); + PTF_ASSERT_TRUE(writerDevNano.open()); + PTF_ASSERT_EQUAL(writerDevNano.getTimestampPrecision(), pcpp::PcapFileWriterDevice::isNanoSecondPrecisionSupported() + ? pcpp::FileTimestampPrecision::Nanoseconds + : pcpp::FileTimestampPrecision::Microseconds, enumclass); + PTF_ASSERT_TRUE(writerDevNano.writePacket(rawPacketMicro)); + PTF_ASSERT_TRUE(writerDevNano.writePacket(rawPacketNano)); + writerDevNano.close(); + + // Write micro precision file + pcpp::PcapFileWriterDevice writerDevMicro(EXAMPLE_PCAP_MICRO_PATH, pcpp::LINKTYPE_ETHERNET, false); + PTF_ASSERT_EQUAL(writerDevMicro.getTimestampPrecision(), pcpp::FileTimestampPrecision::Microseconds, enumclass); + PTF_ASSERT_TRUE(writerDevMicro.open()); + PTF_ASSERT_EQUAL(writerDevMicro.getTimestampPrecision(), pcpp::FileTimestampPrecision::Microseconds, enumclass); + PTF_ASSERT_TRUE(writerDevMicro.writePacket(rawPacketMicro)); + PTF_ASSERT_TRUE(writerDevMicro.writePacket(rawPacketNano)); + writerDevMicro.close(); + + // Read nano precision file + pcpp::PcapFileReaderDevice readerDevNano(EXAMPLE_PCAP_NANO_PATH); + PTF_ASSERT_EQUAL(readerDevNano.getTimestampPrecision(), pcpp::FileTimestampPrecision::Unknown, enumclass); + PTF_ASSERT_TRUE(readerDevNano.open()); + PTF_ASSERT_EQUAL(readerDevNano.getTimestampPrecision(), + pcpp::PcapFileReaderDevice::isNanoSecondPrecisionSupported() + ? pcpp::FileTimestampPrecision::Nanoseconds + : pcpp::FileTimestampPrecision::Microseconds, + enumclass); + + pcpp::RawPacket readPacketNano, readPacketMicro; + PTF_ASSERT_TRUE(readerDevNano.getNextPacket(readPacketMicro)); + PTF_ASSERT_EQUAL(readPacketMicro.getPacketTimeStamp().tv_sec, 1); + PTF_ASSERT_EQUAL(readPacketMicro.getPacketTimeStamp().tv_nsec, 2000); + + PTF_ASSERT_TRUE(readerDevNano.getNextPacket(readPacketNano)); + PTF_ASSERT_EQUAL(readPacketNano.getPacketTimeStamp().tv_sec, 1); + PTF_ASSERT_EQUAL(readPacketNano.getPacketTimeStamp().tv_nsec, pcpp::PcapFileReaderDevice::isNanoSecondPrecisionSupported() ? 1234 : 1000); + + readerDevNano.close(); + + // Read micro precision file + pcpp::PcapFileReaderDevice readerDevMicro(EXAMPLE_PCAP_MICRO_PATH); + PTF_ASSERT_EQUAL(readerDevMicro.getTimestampPrecision(), pcpp::FileTimestampPrecision::Unknown, enumclass); + PTF_ASSERT_TRUE(readerDevMicro.open()); + PTF_ASSERT_EQUAL(readerDevMicro.getTimestampPrecision(), + pcpp::PcapFileReaderDevice::isNanoSecondPrecisionSupported() + ? pcpp::FileTimestampPrecision::Nanoseconds + : pcpp::FileTimestampPrecision::Microseconds, + enumclass); + + pcpp::RawPacket readPacketNano2, readPacketMicro2; + PTF_ASSERT_TRUE(readerDevMicro.getNextPacket(readPacketMicro2)); + PTF_ASSERT_EQUAL(readPacketMicro2.getPacketTimeStamp().tv_sec, 1); + PTF_ASSERT_EQUAL(readPacketMicro2.getPacketTimeStamp().tv_nsec, 2000); + + PTF_ASSERT_TRUE(readerDevMicro.getNextPacket(readPacketNano2)); + PTF_ASSERT_EQUAL(readPacketNano2.getPacketTimeStamp().tv_sec, 1); + PTF_ASSERT_EQUAL(readPacketNano2.getPacketTimeStamp().tv_nsec, 1000); + + readerDevMicro.close(); +} // TestPcapFilePrecision PTF_TEST_CASE(TestPcapSllFileReadWrite) diff --git a/Tests/Pcap++Test/Tests/FilterTests.cpp b/Tests/Pcap++Test/Tests/FilterTests.cpp index 8b12c0f57b..ac22767762 100644 --- a/Tests/Pcap++Test/Tests/FilterTests.cpp +++ b/Tests/Pcap++Test/Tests/FilterTests.cpp @@ -4,6 +4,7 @@ #include "EthLayer.h" #include "VlanLayer.h" #include "IPv4Layer.h" +#include "IPv6Layer.h" #include "TcpLayer.h" #include "UdpLayer.h" #include "PcapLiveDeviceList.h" @@ -453,6 +454,9 @@ PTF_TEST_CASE(TestPcapFiltersOffline) //------------------------- pcpp::IPFilter ipFilterWithMask("212.199.202.9", pcpp::SRC, "255.255.255.0"); ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip and src net 212.199.202.0/24"); + + PTF_ASSERT_RAISES(ipFilterWithMask.setAddr("BogusIPAddressString"), std::invalid_argument, "Not a valid IP address: BogusIPAddressString"); PTF_ASSERT_TRUE(fileReaderDev2.open()); PTF_ASSERT_TRUE(fileReaderDev2.setFilter(ipFilterWithMask)); @@ -470,10 +474,19 @@ PTF_TEST_CASE(TestPcapFiltersOffline) rawPacketVec.clear(); + ipFilterWithMask.clearMask(); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip and src net 212.199.202.9/32"); + + ipFilterWithMask = pcpp::IPFilter(pcpp::IPNetwork("212.199.202.9/24"), pcpp::Direction::DST); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip and dst net 212.199.202.0/24"); + ipFilterWithMask.setDirection(pcpp::Direction::SRC); ipFilterWithMask.setLen(24); ipFilterWithMask.setAddr("212.199.202.9"); ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip and src net 212.199.202.0/24"); PTF_ASSERT_TRUE(fileReaderDev2.open()); PTF_ASSERT_TRUE(fileReaderDev2.setFilter(ipFilterWithMask)); @@ -490,6 +503,70 @@ PTF_TEST_CASE(TestPcapFiltersOffline) } rawPacketVec.clear(); + ipFilterWithMask.clearLen(); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip and src net 212.199.202.9/32"); + + // IPv6 tests + + ipFilterWithMask.setMask("255.255.255.0"); + ipFilterWithMask.setAddr("2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF"); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip6 and src net 2001:d00::/24"); + ipFilterWithMask.clearMask(); + + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip6 and src net 2001:db8:3333:4444:cccc:dddd:eeee:ffff/128"); + + PTF_ASSERT_RAISES(ipFilterWithMask.setMask("255.255.255.255"), std::invalid_argument, "Netmask is not valid IPv6 format: 255.255.255.255"); + ipFilterWithMask.setMask("ffff:ffff:ffff::"); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip6 and src net 2001:db8:3333::/48"); + + ipFilterWithMask.setNetwork(pcpp::IPNetwork("2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF/64")); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip6 and src net 2001:db8:3333:4444::/64"); + + ipFilterWithMask.setLen(48); + ipFilterWithMask.parseToString(filterAsString); + PTF_ASSERT_EQUAL(filterAsString, "ip6 and src net 2001:db8:3333::/48"); + + ipFilterWithMask.setAddr("2001:db8:0:12::1"); + ipFilterWithMask.clearLen(); + ipFilterWithMask.clearMask(); + pcpp::PcapFileReaderDevice fileReaderDev5(EXAMPLE_PCAP_IPV6_PATH); + + PTF_ASSERT_TRUE(fileReaderDev5.open()); + PTF_ASSERT_TRUE(fileReaderDev5.setFilter(ipFilterWithMask)); + fileReaderDev5.getNextPackets(rawPacketVec); + fileReaderDev5.close(); + + PTF_ASSERT_EQUAL(rawPacketVec.size(), 5); + for (pcpp::RawPacketVector::VectorIterator iter = rawPacketVec.begin(); iter != rawPacketVec.end(); iter++) + { + pcpp::Packet packet(*iter); + PTF_ASSERT_TRUE(packet.isPacketOfType(pcpp::IPv6)); + pcpp::IPv6Layer *ipLayer = packet.getLayerOfType(); + // This is essentially matching the host address, but it will have to do for the current sample. + PTF_ASSERT_TRUE(ipLayer->getSrcIPv6Address().matchNetwork("2001:db8:0:12::1/128")); + } + rawPacketVec.clear(); + ipFilterWithMask.setLen(64); + + PTF_ASSERT_TRUE(fileReaderDev5.open()); + PTF_ASSERT_TRUE(fileReaderDev5.setFilter(ipFilterWithMask)); + fileReaderDev5.getNextPackets(rawPacketVec); + fileReaderDev5.close(); + + PTF_ASSERT_EQUAL(rawPacketVec.size(), 10); + for (pcpp::RawPacketVector::VectorIterator iter = rawPacketVec.begin(); iter != rawPacketVec.end(); iter++) + { + pcpp::Packet packet(*iter); + PTF_ASSERT_TRUE(packet.isPacketOfType(pcpp::IPv6)); + pcpp::IPv6Layer *ipLayer = packet.getLayerOfType(); + PTF_ASSERT_TRUE(ipLayer->getSrcIPv6Address().matchNetwork("2001:db8:0:12::/64")); + } + rawPacketVec.clear(); //------------- //Port range @@ -667,20 +744,20 @@ PTF_TEST_CASE(TestPcapFiltersOffline) filterVec.push_back(&protoFilter); pcpp::AndFilter andFilter(filterVec); andFilter.parseToString(filterAsString); - PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6) and (udp)"); + PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6/32) and (udp)"); andFilter.addFilter(&ipFilter); andFilter.parseToString(filterAsString); - PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6) and (udp) and (ip and src net 10.0.0.6)"); + PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6/32) and (udp) and (ip and src net 10.0.0.6/32)"); andFilter.removeFilter(&ipFilter); andFilter.parseToString(filterAsString); - PTF_ASSERT_EQUAL(filterAsString, "(udp) and (ip and src net 10.0.0.6)"); + PTF_ASSERT_EQUAL(filterAsString, "(udp) and (ip and src net 10.0.0.6/32)"); { pcpp::OrFilter externalFilter; andFilter.removeFilter(&externalFilter); - PTF_ASSERT_EQUAL(filterAsString, "(udp) and (ip and src net 10.0.0.6)"); + PTF_ASSERT_EQUAL(filterAsString, "(udp) and (ip and src net 10.0.0.6/32)"); } andFilter.clearAllFilters(); @@ -689,7 +766,7 @@ PTF_TEST_CASE(TestPcapFiltersOffline) andFilter.setFilters(filterVec); andFilter.parseToString(filterAsString); - PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6) and (udp)"); + PTF_ASSERT_EQUAL(filterAsString, "(ip and src net 10.0.0.6/32) and (udp)"); PTF_ASSERT_TRUE(fileReaderDev2.open()); PTF_ASSERT_TRUE(fileReaderDev2.setFilter(andFilter)); @@ -729,7 +806,7 @@ PTF_TEST_CASE(TestPcapFiltersOffline) pcpp::OrFilter orFilter(filterVec); orFilter.parseToString(filterAsString); - PTF_ASSERT_EQUAL(filterAsString, "(arp) or ((proto 47) and (ip and src or dst net 20.0.0.1))"); + PTF_ASSERT_EQUAL(filterAsString, "(arp) or ((proto 47) and (ip and src or dst net 20.0.0.1/32))"); PTF_ASSERT_TRUE(fileReaderDev3.open()); PTF_ASSERT_TRUE(fileReaderDev3.setFilter(orFilter)); diff --git a/Tests/Pcap++Test/Tests/IpMacTests.cpp b/Tests/Pcap++Test/Tests/IpMacTests.cpp index 404782c816..88a09a4760 100644 --- a/Tests/Pcap++Test/Tests/IpMacTests.cpp +++ b/Tests/Pcap++Test/Tests/IpMacTests.cpp @@ -8,7 +8,9 @@ #include "EndianPortable.h" #include "Logger.h" #include "GeneralUtils.h" +#include "IpUtils.h" #include "IpAddress.h" +#include "IpAddressUtils.h" #include "MacAddress.h" #include "LRUList.h" #include "NetworkUtils.h" @@ -39,13 +41,43 @@ PTF_TEST_CASE(TestIPAddress) secondIPv4Address = ip4AddrFromIpAddr; PTF_ASSERT_EQUAL(ip4AddrFromIpAddr, secondIPv4Address); + { + in_addr inAddr_v4; + PTF_ASSERT_EQUAL(inet_pton(AF_INET, "10.0.0.4", &inAddr_v4), 1); + + // Equality between equal in_addr and IPv4Address. + PTF_ASSERT_TRUE(ip4AddrFromIpAddr == inAddr_v4); + PTF_ASSERT_TRUE(inAddr_v4 == ip4AddrFromIpAddr); + PTF_ASSERT_FALSE(ip4AddrFromIpAddr != inAddr_v4); + PTF_ASSERT_FALSE(inAddr_v4 != ip4AddrFromIpAddr); + + // Equality between equal in_addr and IPAddress. + PTF_ASSERT_TRUE(ip4Addr == inAddr_v4); + PTF_ASSERT_TRUE(inAddr_v4 == ip4Addr); + PTF_ASSERT_FALSE(ip4Addr != inAddr_v4); + PTF_ASSERT_FALSE(inAddr_v4 != ip4Addr); + + PTF_ASSERT_EQUAL(inet_pton(AF_INET, "10.0.1.4", &inAddr_v4), 1); + // Equality between different in_addr and IPv4Address. + PTF_ASSERT_FALSE(ip4AddrFromIpAddr == inAddr_v4); + PTF_ASSERT_FALSE(inAddr_v4 == ip4AddrFromIpAddr); + PTF_ASSERT_TRUE(ip4AddrFromIpAddr != inAddr_v4); + PTF_ASSERT_TRUE(inAddr_v4 != ip4AddrFromIpAddr); + + // Equality between different in_addr and IPAddress. + PTF_ASSERT_FALSE(ip4Addr == inAddr_v4); + PTF_ASSERT_FALSE(inAddr_v4 == ip4Addr); + PTF_ASSERT_TRUE(ip4Addr != inAddr_v4); + PTF_ASSERT_TRUE(inAddr_v4 != ip4Addr); + } + // networks pcpp::IPv4Address ipv4Addr("10.0.0.4"); auto networks = std::vector>{ std::tuple{"10.8.0.0", "8", "255.0.0.0"}, std::tuple{"10.0.0.0", "24", "255.255.255.0"} }; - for (auto network : networks) + for (const auto& network : networks) { std::string networkWithPrefixAsString = std::get<0>(network) + "/" + std::get<1>(network); std::string networkWithMaskAsString = std::get<0>(network) + "/" + std::get<2>(network); @@ -56,7 +88,7 @@ PTF_TEST_CASE(TestIPAddress) pcpp::Logger::getInstance().suppressLogs(); auto invalidMasks = std::vector{"aaaa", "10.0.0.0", "10.0.0.0/aa", "10.0.0.0/33", "999.999.1.1/24", "10.10.10.10/99.99.99"}; - for (auto invalidMask : invalidMasks) + for (const auto& invalidMask : invalidMasks) { PTF_ASSERT_FALSE(ipv4Addr.matchNetwork(invalidMask)); } @@ -88,6 +120,35 @@ PTF_TEST_CASE(TestIPAddress) PTF_ASSERT_EQUAL(addrAsByteArray[i], expectedByteArray[i]); } + { + in6_addr in_ipv6_addr; + PTF_ASSERT_EQUAL(inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &in_ipv6_addr), 1); + + // Equality between equal in6_addr and IPv6Address. + PTF_ASSERT_TRUE(ip6AddrFromIpAddr == in_ipv6_addr); + PTF_ASSERT_TRUE(in_ipv6_addr == ip6AddrFromIpAddr); + PTF_ASSERT_FALSE(ip6AddrFromIpAddr != in_ipv6_addr); + PTF_ASSERT_FALSE(in_ipv6_addr != ip6AddrFromIpAddr); + + // Equality between equal in6_addr and IPAddress. + PTF_ASSERT_TRUE(ip6Addr == in_ipv6_addr); + PTF_ASSERT_TRUE(in_ipv6_addr == ip6Addr); + PTF_ASSERT_FALSE(ip6Addr != in_ipv6_addr); + PTF_ASSERT_FALSE(in_ipv6_addr != ip6Addr); + + PTF_ASSERT_EQUAL(inet_pton(AF_INET6, "2607:f0d0:1002:51:4::4", &in_ipv6_addr), 1); + PTF_ASSERT_FALSE(ip6AddrFromIpAddr == in_ipv6_addr); + PTF_ASSERT_FALSE(in_ipv6_addr == ip6AddrFromIpAddr); + PTF_ASSERT_TRUE(ip6AddrFromIpAddr != in_ipv6_addr); + PTF_ASSERT_TRUE(in_ipv6_addr != ip6AddrFromIpAddr); + + // Equality between different in6_addr and IPAddress. + PTF_ASSERT_FALSE(ip6Addr == in_ipv6_addr); + PTF_ASSERT_FALSE(in_ipv6_addr == ip6Addr); + PTF_ASSERT_TRUE(ip6Addr != in_ipv6_addr); + PTF_ASSERT_TRUE(in_ipv6_addr != ip6Addr); + } + ip6Addr = pcpp::IPAddress("2607:f0d0:1002:0051:0000:0000:0000:0004"); PTF_ASSERT_EQUAL(ip6Addr.getType(), pcpp::IPAddress::IPv6AddressType, enum); PTF_ASSERT_EQUAL(ip6Addr.toString(), "2607:f0d0:1002:51::4"); @@ -109,7 +170,7 @@ PTF_TEST_CASE(TestIPAddress) std::tuple{0, "0", "::"} }; - for (auto ipv6Network : ipv6Networks) + for (const auto& ipv6Network : ipv6Networks) { PTF_ASSERT_TRUE(ip6Addr2.matchNetwork(pcpp::IPv6Network(ipv6NetworkPrefix, std::get<0>(ipv6Network)))); @@ -124,7 +185,7 @@ PTF_TEST_CASE(TestIPAddress) std::tuple{128, "128", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"} }; - for (auto ipv6Network : ipv6NetworksNotMatch) + for (const auto& ipv6Network : ipv6NetworksNotMatch) { PTF_ASSERT_FALSE(ip6Addr2.matchNetwork(pcpp::IPv6Network(ipv6NetworkPrefix, std::get<0>(ipv6Network)))); @@ -355,23 +416,23 @@ PTF_TEST_CASE(TestIPv4Network) PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), 33), std::invalid_argument, "prefixLen must be an integer between 0 and 32"); // Invalid c'tor: IPv4 address + netmask - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "invalid"), std::invalid_argument, "Netmask is not valid: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "999.999.999.999"), std::invalid_argument, "Netmask is not valid: 999.999.999.999"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "255.255.0.255"), std::invalid_argument, "Netmask is not valid: 255.255.0.255"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "10.10.10.10"), std::invalid_argument, "Netmask is not valid: 10.10.10.10"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "0.255.255.255"), std::invalid_argument, "Netmask is not valid: 0.255.255.255"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "127.255.255.255"), std::invalid_argument, "Netmask is not valid: 127.255.255.255"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "invalid"), std::invalid_argument, "Netmask is not valid IPv4 format: invalid"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "999.999.999.999"), std::invalid_argument, "Netmask is not valid IPv4 format: 999.999.999.999"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "255.255.0.255"), std::invalid_argument, "Netmask is not valid IPv4 format: 255.255.0.255"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "10.10.10.10"), std::invalid_argument, "Netmask is not valid IPv4 format: 10.10.10.10"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "0.255.255.255"), std::invalid_argument, "Netmask is not valid IPv4 format: 0.255.255.255"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(pcpp::IPv4Address("1.1.1.1"), "127.255.255.255"), std::invalid_argument, "Netmask is not valid IPv4 format: 127.255.255.255"); // Invalid c'tor: address + netmask in one string PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("invalid")), std::invalid_argument, "The input should be in the format of
/ or
/"); PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("invalid/255.255.255.0")), std::invalid_argument, "The input doesn't contain a valid IPv4 network prefix: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/255.255.255.0/24")), std::invalid_argument, "Netmask is not valid: 255.255.255.0/24"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/255.255.255.0/24")), std::invalid_argument, "Netmask is not valid IPv4 format: 255.255.255.0/24"); PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/33")), std::invalid_argument, "Prefix length must be an integer between 0 and 32"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/-1")), std::invalid_argument, "Netmask is not valid: -1"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/invalid")), std::invalid_argument, "Netmask is not valid: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/999.999.999.999")), std::invalid_argument, "Netmask is not valid: 999.999.999.999"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/255.255.0.1")), std::invalid_argument, "Netmask is not valid: 255.255.0.1"); - PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/0.0.255.255")), std::invalid_argument, "Netmask is not valid: 0.0.255.255"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/-1")), std::invalid_argument, "Netmask is not valid IPv4 format: -1"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/invalid")), std::invalid_argument, "Netmask is not valid IPv4 format: invalid"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/999.999.999.999")), std::invalid_argument, "Netmask is not valid IPv4 format: 999.999.999.999"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/255.255.0.1")), std::invalid_argument, "Netmask is not valid IPv4 format: 255.255.0.1"); + PTF_ASSERT_RAISES(pcpp::IPv4Network(std::string("1.1.1.1/0.0.255.255")), std::invalid_argument, "Netmask is not valid IPv4 format: 0.0.255.255"); // Valid c'tor auto addressAsStr = std::string("192.168.10.100"); @@ -451,24 +512,24 @@ PTF_TEST_CASE(TestIPv6Network) PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), 129), std::invalid_argument, "prefixLen must be an integer between 0 and 128"); // Invalid c'tor: IPv6 address + netmask - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "invalid"), std::invalid_argument, "Netmask is not valid: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff:ff10::"), std::invalid_argument, "Netmask is not valid: ffff:ff10::"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff:ee00::"), std::invalid_argument, "Netmask is not valid: ffff:ee00::"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "7f00::"), std::invalid_argument, "Netmask is not valid: 7f00::"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff::ffff"), std::invalid_argument, "Netmask is not valid: ffff::ffff"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "f000::0001"), std::invalid_argument, "Netmask is not valid: f000::0001"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "invalid"), std::invalid_argument, "Netmask is not valid IPv6 format: invalid"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff:ff10::"), std::invalid_argument, "Netmask is not valid IPv6 format: ffff:ff10::"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff:ee00::"), std::invalid_argument, "Netmask is not valid IPv6 format: ffff:ee00::"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "7f00::"), std::invalid_argument, "Netmask is not valid IPv6 format: 7f00::"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "ffff::ffff"), std::invalid_argument, "Netmask is not valid IPv6 format: ffff::ffff"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(pcpp::IPv6Address("2001:db8::"), "f000::0001"), std::invalid_argument, "Netmask is not valid IPv6 format: f000::0001"); // Invalid c'tor: address + netmask in one string PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("invalid")), std::invalid_argument, "The input should be in the format of
/ or
/"); PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("invalid/32")), std::invalid_argument, "The input doesn't contain a valid IPv6 network prefix: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/32/24")), std::invalid_argument, "Netmask is not valid: 32/24"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/255.255.0.0")), std::invalid_argument, "Netmask is not valid: 255.255.0.0"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/32/24")), std::invalid_argument, "Netmask is not valid IPv6 format: 32/24"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/255.255.0.0")), std::invalid_argument, "Netmask is not valid IPv6 format: 255.255.0.0"); PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/130")), std::invalid_argument, "Prefix length must be an integer between 0 and 128"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/-1")), std::invalid_argument, "Netmask is not valid: -1"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/invalid")), std::invalid_argument, "Netmask is not valid: invalid"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/a2cb:d625::")), std::invalid_argument, "Netmask is not valid: a2cb:d625::"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/ffff::0001")), std::invalid_argument, "Netmask is not valid: ffff::0001"); - PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/0fff::")), std::invalid_argument, "Netmask is not valid: 0fff::"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/-1")), std::invalid_argument, "Netmask is not valid IPv6 format: -1"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/invalid")), std::invalid_argument, "Netmask is not valid IPv6 format: invalid"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/a2cb:d625::")), std::invalid_argument, "Netmask is not valid IPv6 format: a2cb:d625::"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/ffff::0001")), std::invalid_argument, "Netmask is not valid IPv6 format: ffff::0001"); + PTF_ASSERT_RAISES(pcpp::IPv6Network(std::string("ef3c:7157:a084:23c0::/0fff::")), std::invalid_argument, "Netmask is not valid IPv6 format: 0fff::"); // Valid c'tor auto addressAsStr = std::string("39e1:f90e:14dd:f9a1:4d0a:7f9f:da18:5746"); diff --git a/Tests/Pcap++Test/Tests/KniTests.cpp b/Tests/Pcap++Test/Tests/KniTests.cpp index 75f1be64f8..09563619f2 100644 --- a/Tests/Pcap++Test/Tests/KniTests.cpp +++ b/Tests/Pcap++Test/Tests/KniTests.cpp @@ -23,7 +23,7 @@ struct KniRequestsCallbacksMock static bool onPacketsCallbackSingleBurst(pcpp::MBufRawPacket*, uint32_t numOfPackets, pcpp::KniDevice*, void* userCookie) { - unsigned int* counter = (unsigned int*)userCookie; + unsigned int* counter = static_cast(userCookie); *counter = numOfPackets; // Break after first burst return false; @@ -34,7 +34,7 @@ struct KniRequestsCallbacksMock } static bool onPacketsCallback(pcpp::MBufRawPacket*, uint32_t numOfPackets, pcpp::KniDevice*, void* userCookie) { - unsigned int* counter = (unsigned int*)userCookie; + unsigned int* counter = static_cast(userCookie); *counter = *counter + numOfPackets; return true; } diff --git a/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp b/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp index 38950e39eb..9a93989704 100644 --- a/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp +++ b/Tests/Pcap++Test/Tests/LiveDeviceTests.cpp @@ -10,7 +10,8 @@ #include "../Common/GlobalTestArgs.h" #include "../Common/TestUtils.h" #include "../Common/PcapFileNamesDef.h" -#include +#include +#include #if defined(_WIN32) #include "PcapRemoteDevice.h" #include "PcapRemoteDeviceList.h" @@ -22,12 +23,12 @@ extern PcapTestArgs PcapTestGlobalArgs; static void packetArrives(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* pDevice, void* userCookie) { - (*(int*)userCookie)++; + (*static_cast(userCookie))++; } static void statsUpdate(pcpp::IPcapDevice::PcapStats& stats, void* userCookie) { - (*(int*)userCookie)++; + (*static_cast(userCookie))++; } static bool packetArrivesBlockingModeTimeout(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* userCookie) @@ -37,7 +38,7 @@ static bool packetArrivesBlockingModeTimeout(pcpp::RawPacket* rawPacket, pcpp::P static bool packetArrivesBlockingModeNoTimeout(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* userCookie) { - int* packetCount = (int*)userCookie; + int* packetCount = static_cast(userCookie); if ((*packetCount) == 5) return true; @@ -57,7 +58,7 @@ static bool packetArrivesBlockingModeStartCapture(pcpp::RawPacket* rawPacket, pc pcpp::Logger::getInstance().enableLogs(); - int* packetCount = (int*)userCookie; + int* packetCount = static_cast(userCookie); if ((*packetCount) == 5) return true; @@ -70,7 +71,7 @@ static bool packetArrivesBlockingModeStopCapture(pcpp::RawPacket* rawPacket, pcp // shouldn't do anything dev->stopCapture(); - int* packetCount = (int*)userCookie; + int* packetCount = static_cast(userCookie); if ((*packetCount) == 5) return true; @@ -80,14 +81,14 @@ static bool packetArrivesBlockingModeStopCapture(pcpp::RawPacket* rawPacket, pcp static bool packetArrivesBlockingModeNoTimeoutPacketCount(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* userCookie) { - int* packetCount = (int*)userCookie; + int* packetCount = static_cast(userCookie); (*packetCount)++; return false; } static bool packetArrivesBlockingModeWithSnaplen(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* dev, void* userCookie) { - int snaplen = *(int*)userCookie; + int snaplen = *static_cast(userCookie); return rawPacket->getRawDataLen() > snaplen; } @@ -97,17 +98,60 @@ class RpcapdServerInitializer { private: HANDLE m_ProcessHandle; + HANDLE m_JobHandle; -public: + void killProcessAndCloseHandles() + { + if (m_ProcessHandle != nullptr) + { + TerminateProcess(m_ProcessHandle, 0); + CloseHandle(m_ProcessHandle); + m_ProcessHandle = nullptr; + } - RpcapdServerInitializer(bool activateRemoteDevice, const std::string &ip, uint16_t port) : m_ProcessHandle(nullptr) + if (m_JobHandle != nullptr) + { + CloseHandle(m_JobHandle); + m_JobHandle = nullptr; + } + } +public: + RpcapdServerInitializer(bool activateRemoteDevice, const std::string& ip, uint16_t port) + : m_ProcessHandle(nullptr), m_JobHandle(nullptr) { if (!activateRemoteDevice) return; std::string cmd = "rpcapd\\rpcapd.exe"; - std::ostringstream args; - args << "rpcapd\\rpcapd.exe -b " << ip << " -p " << port << " -n"; + std::array args; + int res = std::snprintf(args.data(), args.size(), "%s -b %s -p %d -n", cmd.c_str(), ip.c_str(), port); + if (res < 0) + { + throw std::runtime_error("Error during string formatting"); + } + else if (res >= args.size()) + { + // We should never get here with reasonable input but you never know. + throw std::runtime_error("Buffer overflow. Are you sure the IP is a valid string?"); + } + + m_JobHandle = CreateJobObject(nullptr, nullptr); + if (m_JobHandle == nullptr) + { + throw std::runtime_error("Failed to create a job object with error code: " + std::to_string(GetLastError())); + } + + // Sets up the job limits so closing the job will automatically kill all processes assigned to the job. + // This will prevent the subprocess continuing to live if the current process is killed without unwinding the stack (i.e. std::terminate is called), + // as the OS itself will kill the subprocesses when the last job handle is closed. + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobLimitInfo{}; + jobLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (!SetInformationJobObject(m_JobHandle, JobObjectExtendedLimitInformation, &jobLimitInfo, sizeof(jobLimitInfo))) + { + DWORD errCode = GetLastError(); + CloseHandle(m_JobHandle); + throw std::runtime_error("Failed to set settings to job object with error code: " + std::to_string(errCode)); + } STARTUPINFO si; PROCESS_INFORMATION pi; @@ -115,35 +159,54 @@ class RpcapdServerInitializer ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); - if (!CreateProcess - ( + if (!CreateProcess ( TEXT(cmd.c_str()), - (char*)TEXT(args.str().c_str()), - NULL,NULL,FALSE, + // CreateProcessW (Unicode version) modifies the argument string inplace during internal processing. + TEXT(args.data()), + nullptr, nullptr, false, CREATE_NEW_CONSOLE, - NULL,NULL, + nullptr, nullptr, &si, &pi - ) - ) - { - m_ProcessHandle = NULL; - PCPP_LOG_ERROR("Create process failed " << (int)GetLastError()); - return; - } + )) + { + DWORD errCode = GetLastError(); + CloseHandle(m_JobHandle); + throw std::runtime_error("Create process failed with error code: " + std::to_string(errCode)); + } m_ProcessHandle = pi.hProcess; - } + CloseHandle(pi.hThread); // We don't need the thread handle, so we can close it. - ~RpcapdServerInitializer() - { - if (m_ProcessHandle != NULL) + if (!AssignProcessToJobObject(m_JobHandle, m_ProcessHandle)) { - TerminateProcess(m_ProcessHandle, 0); + DWORD errCode = GetLastError(); + killProcessAndCloseHandles(); + throw std::runtime_error("Failed assigning process to job object with code: " + std::to_string(errCode)); } } - HANDLE getHandle() { return m_ProcessHandle; } + RpcapdServerInitializer(const RpcapdServerInitializer&) = delete; + RpcapdServerInitializer(RpcapdServerInitializer&& other) noexcept + : m_ProcessHandle(other.m_ProcessHandle), m_JobHandle(other.m_JobHandle) + { + other.m_ProcessHandle = nullptr; + other.m_JobHandle = nullptr; + } + RpcapdServerInitializer& operator=(const RpcapdServerInitializer&) = delete; + RpcapdServerInitializer& operator=(RpcapdServerInitializer&& other) noexcept + { + killProcessAndCloseHandles(); + m_ProcessHandle = other.m_ProcessHandle; + m_JobHandle = other.m_JobHandle; + other.m_ProcessHandle = nullptr; + other.m_JobHandle = nullptr; + return *this; + } + + ~RpcapdServerInitializer() { killProcessAndCloseHandles(); } + + HANDLE getHandle() const { return m_ProcessHandle; } }; #endif // defined(_WIN32) @@ -229,7 +292,7 @@ PTF_TEST_CASE(TestPcapLiveDevice) DeviceTeardown devTeardown(liveDev); int packetCount = 0; int numOfTimeStatsWereInvoked = 0; - PTF_ASSERT_TRUE(liveDev->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_TRUE(liveDev->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); int totalSleepTime = 0; while (totalSleepTime <= 20) { @@ -254,7 +317,7 @@ PTF_TEST_CASE(TestPcapLiveDevice) // a negative test pcpp::Logger::getInstance().suppressLogs(); - PTF_ASSERT_FALSE(liveDev->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_FALSE(liveDev->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); pcpp::Logger::getInstance().enableLogs(); } // TestPcapLiveDevice @@ -265,14 +328,32 @@ PTF_TEST_CASE(TestPcapLiveDeviceClone) // Test of clone device should be same with original pcpp::PcapLiveDevice* liveDev = nullptr; pcpp::IPv4Address ipToSearch(PcapTestGlobalArgs.ipToSendReceivePackets.c_str()); - liveDev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(ipToSearch)->clone(); + + { + pcpp::PcapLiveDevice* originalDev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(ipToSearch); + PTF_ASSERT_NOT_NULL(originalDev); + +#ifdef _WIN32 + // Tests if device pointer points to a Windows Live Device on a Windows machine. + PTF_ASSERT_NOT_NULL(dynamic_cast(originalDev)); +#endif // _WIN32 + + liveDev = originalDev->clone(); + } + PTF_ASSERT_NOT_NULL(liveDev); + +#ifdef _WIN32 + // Tests if the clone is correctly returns a Windows Live Device on Windows systems. + PTF_ASSERT_NOT_NULL(dynamic_cast(liveDev)); +#endif // _WIN32 + PTF_ASSERT_GREATER_THAN(liveDev->getMtu(), 0); PTF_ASSERT_TRUE(liveDev->open()); DeviceTeardown devTeardown(liveDev, true); int packetCount = 0; int numOfTimeStatsWereInvoked = 0; - PTF_ASSERT_TRUE(liveDev->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_TRUE(liveDev->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); int totalSleepTime = 0; while (totalSleepTime <= 20) { @@ -296,7 +377,7 @@ PTF_TEST_CASE(TestPcapLiveDeviceClone) // a negative test pcpp::Logger::getInstance().suppressLogs(); - PTF_ASSERT_FALSE(liveDev->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_FALSE(liveDev->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); pcpp::Logger::getInstance().enableLogs(); } // TestPcapLiveDeviceClone @@ -343,7 +424,7 @@ PTF_TEST_CASE(TestPcapLiveDeviceStatsMode) PTF_ASSERT_TRUE(liveDev->open()); DeviceTeardown devTeardown(liveDev); int numOfTimeStatsWereInvoked = 0; - PTF_ASSERT_TRUE(liveDev->startCapture(1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_TRUE(liveDev->startCapture(1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); sendURLRequest("www.ebay.com"); int totalSleepTime = 0; while (totalSleepTime <= 6) @@ -370,7 +451,7 @@ PTF_TEST_CASE(TestPcapLiveDeviceStatsMode) // a negative test pcpp::Logger::getInstance().suppressLogs(); - PTF_ASSERT_FALSE(liveDev->startCapture(1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_FALSE(liveDev->startCapture(1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); pcpp::Logger::getInstance().enableLogs(); } // TestPcapLiveDeviceStatsMode @@ -491,15 +572,15 @@ PTF_TEST_CASE(TestPcapLiveDeviceWithLambda) auto packetArrivesLambda = [](pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* pDevice, void* userCookie) { - (*(int*)userCookie)++; + (*static_cast(userCookie))++; }; auto statsUpdateLambda = [](pcpp::IPcapDevice::PcapStats& stats, void* userCookie) { - (*(int*)userCookie)++; + (*static_cast(userCookie))++; }; - PTF_ASSERT_TRUE(liveDev->startCapture(packetArrivesLambda , (void*)&packetCount, 1, statsUpdateLambda, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_TRUE(liveDev->startCapture(packetArrivesLambda , static_cast(&packetCount), 1, statsUpdateLambda, static_cast(&numOfTimeStatsWereInvoked))); int totalSleepTime = 0; while (totalSleepTime <= 20) { @@ -523,7 +604,7 @@ PTF_TEST_CASE(TestPcapLiveDeviceBlockingModeWithLambda) auto packetArrivesBlockingModeNoTimeoutLambda = []( pcpp::RawPacket *rawPacket, pcpp::PcapLiveDevice *dev, void *userCookie) { - int *packetCount = (int *)userCookie; + int *packetCount = static_cast(userCookie); if ((*packetCount) == 5) return true; @@ -632,7 +713,7 @@ PTF_TEST_CASE(TestWinPcapLiveDevice) PTF_ASSERT_TRUE(winPcapLiveDevice->setMinAmountOfDataToCopyFromKernelToApplication(100000)); int packetCount = 0; int numOfTimeStatsWereInvoked = 0; - PTF_ASSERT_TRUE(winPcapLiveDevice->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_TRUE(winPcapLiveDevice->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); for (int i = 0; i < 5; i++) { sendURLRequest("www.ebay.com"); @@ -650,7 +731,7 @@ PTF_TEST_CASE(TestWinPcapLiveDevice) // a negative test pcpp::Logger::getInstance().suppressLogs(); - PTF_ASSERT_FALSE(winPcapLiveDevice->startCapture(&packetArrives, (void*)&packetCount, 1, &statsUpdate, (void*)&numOfTimeStatsWereInvoked)); + PTF_ASSERT_FALSE(winPcapLiveDevice->startCapture(&packetArrives, static_cast(&packetCount), 1, &statsUpdate, static_cast(&numOfTimeStatsWereInvoked))); pcpp::Logger::getInstance().enableLogs(); #else @@ -867,6 +948,7 @@ PTF_TEST_CASE(TestRemoteCapture) PTF_ASSERT_EQUAL(remoteDevices->getRemoteMachinePort(), remoteDevicePort); pcpp::PcapRemoteDevice* remoteDevice = remoteDevices->getRemoteDeviceByIP(remoteDeviceIPAddr); + PTF_ASSERT_NOT_NULL(remoteDevice); PTF_ASSERT_EQUAL(remoteDevice->getDeviceType(), pcpp::PcapLiveDevice::RemoteDevice, enum); PTF_ASSERT_EQUAL(remoteDevice->getMtu(), 0); pcpp::Logger::getInstance().suppressLogs(); diff --git a/Tests/Pcap++Test/Tests/PfRingTests.cpp b/Tests/Pcap++Test/Tests/PfRingTests.cpp index a4226e5173..b12828beda 100644 --- a/Tests/Pcap++Test/Tests/PfRingTests.cpp +++ b/Tests/Pcap++Test/Tests/PfRingTests.cpp @@ -42,7 +42,7 @@ struct SetFilterInstruction static void pfRingPacketsArrive(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, pcpp::PfRingDevice* device, void* userCookie) { - PfRingPacketData* data = (PfRingPacketData*)userCookie; + PfRingPacketData* data = static_cast(userCookie); data->ThreadId = threadId; data->PacketCount += numOfPackets; @@ -64,7 +64,7 @@ static void pfRingPacketsArrive(pcpp::RawPacket* packets, uint32_t numOfPackets, static void pfRingPacketsArriveMultiThread(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, pcpp::PfRingDevice* device, void* userCookie) { - PfRingPacketData* data = (PfRingPacketData*)userCookie; + PfRingPacketData* data = static_cast(userCookie); data[threadId].ThreadId = threadId; data[threadId].PacketCount += numOfPackets; @@ -93,7 +93,7 @@ static void pfRingPacketsArriveMultiThread(pcpp::RawPacket* packets, uint32_t nu void pfRingPacketsArriveSetFilter(pcpp::RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, pcpp::PfRingDevice* device, void* userCookie) { - SetFilterInstruction* instruction = (SetFilterInstruction*)userCookie; + SetFilterInstruction* instruction = static_cast(userCookie); switch(instruction->Instruction) { case 1: //verify TCP packet diff --git a/Tests/Pcap++Test/main.cpp b/Tests/Pcap++Test/main.cpp index 6ba1ee4c08..4c38c16223 100644 --- a/Tests/Pcap++Test/main.cpp +++ b/Tests/Pcap++Test/main.cpp @@ -212,6 +212,7 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(TestLoggerMultiThread, "no_network;logger;skip_mem_leak_check"); PTF_RUN_TEST(TestPcapFileReadWrite, "no_network;pcap"); + PTF_RUN_TEST(TestPcapFilePrecision, "no_network;pcap"); PTF_RUN_TEST(TestPcapSllFileReadWrite, "no_network;pcap"); PTF_RUN_TEST(TestPcapSll2FileReadWrite, "no_network;pcap"); PTF_RUN_TEST(TestPcapRawIPFileReadWrite, "no_network;pcap"); diff --git a/Tests/PcppTestFramework/PcppTestFramework.h b/Tests/PcppTestFramework/PcppTestFramework.h index 1041f09d35..648dceeb73 100644 --- a/Tests/PcppTestFramework/PcppTestFramework.h +++ b/Tests/PcppTestFramework/PcppTestFramework.h @@ -15,6 +15,13 @@ #define ptr_PTF_PRINT_TYPE_EXPECTED(exp, val) exp << "[ptr: " << val << "]" #define enumclass_PTF_PRINT_TYPE_ACTUAL(exp, val) "enum[" << +static_cast::type>(val) << "]" #define enumclass_PTF_PRINT_TYPE_EXPECTED(exp, val) exp << "[" << +static_cast::type>(val) << "]" +#define BUFFER_PRINT(buffer, size) \ + for(size_t currentByteId = 0; currentByteId < static_cast(size); ++currentByteId) { \ + std::cout << std::setfill('0') << std::setw(2) << std::right << std::hex << static_cast(*(buffer + currentByteId)) << " "; \ + } \ + std::cout << std::setfill(' ') << std::setw(0) << std::left << std::dec; \ + std::cout << std::endl + #define PTF_PRINT_ASSERTION(severity, op) \ std::cout << std::left << std::setw(35) << __FUNCTION__ << ": " \ @@ -68,6 +75,30 @@ } \ } +#define PTF_ASSERT_VECTORS_EQUAL(actual, expected, ...) \ + { \ + if (actual != expected) {\ + std::ostringstream actualOss, expectedOss; \ + bool first = true; \ + for (const auto& elem : actual) { \ + if (!first) actualOss << ", "; \ + actualOss << elem; \ + first = false; \ + } \ + first = true; \ + for (const auto& elem : expected) { \ + if (!first) expectedOss << ", "; \ + expectedOss << elem; \ + first = false; \ + } \ + std::string actualValues = "[" + actualOss.str() + "]"; \ + std::string expectedValues = "[" + expectedOss.str() + "]"; \ + PTF_PRINT_COMPARE_ASSERTION_FAILED("VECTORS EQUAL", #actual, actualValues, #expected, expectedValues, __VA_ARGS__); \ + ptfResult = PTF_RESULT_FAILED; \ + return; \ + } \ + } + #define PTF_ASSERT_NOT_EQUAL(actual, expected, ...) \ { \ auto ptfActual = actual; \ @@ -126,10 +157,10 @@ #define PTF_ASSERT_BUF_COMPARE(buf1, buf2, size) \ if (memcmp(buf1, buf2, size) != 0) { \ PTF_PRINT_ASSERTION("FAILED", "BUFFER COMPARE") \ - << " [ " << #buf1 << " ]" << std::endl \ - << " <>" << std::endl \ - << " [ " << #buf2 << " ]" \ - << std::endl; \ + << " Actual " << std::endl; \ + BUFFER_PRINT(buf1, size) \ + << " Expected " << std::endl; \ + BUFFER_PRINT(buf2, size); \ ptfResult = PTF_RESULT_FAILED; \ return; \ }